| File: | lib/Parrot/Configure/Compiler.pm |
| Coverage: | 90.1% |
| line | stmt | bran | cond | sub | code |
|---|---|---|---|---|---|
| 1 | # Copyright (C) 2001-2012, Parrot Foundation. | ||||
| 2 | |||||
| 3 - 19 | =head1 NAME Parrot::Configure::Compiler - C-Related methods for configuration and more =head1 DESCRIPTION The Parrot::Configure::Compiler module provides methods inherited by Parrot::Configure which prepare and/or run C programs during compilation. Other methods from this module will be used to generate makefiles and other files. Template entries of the form C<@key@> will be replaced with C<key>'s value from the configuration system's data. =head2 Methods =over 4 =cut | ||||
| 20 | |||||
| 21 | package Parrot::Configure::Compiler; | ||||
| 22 | |||||
| 23 | 99 99 99 | use strict; | |||
| 24 | 99 99 99 | use warnings; | |||
| 25 | |||||
| 26 | 99 99 99 | use Carp; | |||
| 27 | 99 99 99 | use File::Spec (); | |||
| 28 | 99 99 99 | use lib ('lib'); | |||
| 29 | 99 | use Parrot::Configure::Utils qw( | |||
| 30 | prompt copy_if_diff move_if_diff integrate | ||||
| 31 | capture_output check_progs _slurp | ||||
| 32 | _run_command _build_compile_command | ||||
| 33 | move_if_diff | ||||
| 34 | 99 99 | ); | |||
| 35 | |||||
| 36 | # report the makefile and lineno | ||||
| 37 | sub makecroak { | ||||
| 38 | 1 | my ($conf, $error) = @_; | |||
| 39 | 1 | my ($file, $line) = ($conf->{_compiler_file}, $conf->{_compiler_line}); | |||
| 40 | 1 | die "$error at $file line $line\n"; | |||
| 41 | } | ||||
| 42 | |||||
| 43 | our %file_types_info = ( | ||||
| 44 | makefile => { | ||||
| 45 | comment_type => '#', | ||||
| 46 | }, | ||||
| 47 | c => { | ||||
| 48 | comment_type => '/*', | ||||
| 49 | }, | ||||
| 50 | pmc => { | ||||
| 51 | comment_type => '/*', | ||||
| 52 | }, | ||||
| 53 | perl => { | ||||
| 54 | comment_type => '#', | ||||
| 55 | }, | ||||
| 56 | pir => { | ||||
| 57 | comment_type => '#', | ||||
| 58 | }, | ||||
| 59 | ); | ||||
| 60 | |||||
| 61 - 67 | =item C<cc_gen()>
$conf->cc_gen($source)
Generates F<test_$$.c> from the specified source file.
=cut | ||||
| 68 | |||||
| 69 | sub cc_gen { | ||||
| 70 | 273 | my $conf = shift; | |||
| 71 | 273 | my $source = shift; | |||
| 72 | |||||
| 73 | 273 | $conf->genfile( $source, "test_$$.c", file_type => 'c' ); | |||
| 74 | } | ||||
| 75 | |||||
| 76 - 86 | =item C<cc_build()>
$conf->cc_build($cc_args, $link_args)
These items are used from current config settings:
$cc, $ccflags, $ldout, $o, $link, $linkflags, $cc_exe_out, $exe, $libs
Calls the compiler and linker on F<test_$$.c>.
=cut | ||||
| 87 | |||||
| 88 | sub cc_build { | ||||
| 89 | 103 | my $conf = shift; | |||
| 90 | 103 | my ( $cc_args, $link_args ) = @_; | |||
| 91 | |||||
| 92 | 103 | $cc_args = '' unless defined $cc_args; | |||
| 93 | 103 | $link_args = '' unless defined $link_args; | |||
| 94 | |||||
| 95 | 103 | my $verbose = $conf->options->get('verbose'); | |||
| 96 | |||||
| 97 | 103 | my ( $cc, $ccflags, $ldout, $o, $link, $linkflags, $cc_exe_out, $exe, $libs ) = | |||
| 98 | $conf->data->get(qw(cc ccflags ld_out o link linkflags cc_exe_out exe libs)); | ||||
| 99 | |||||
| 100 | # unique test file name for parallel builds | ||||
| 101 | 103 | my $test = 'test_' . $$; | |||
| 102 | 103 | my $compile_command = _build_compile_command( $cc, $ccflags, $cc_args ); | |||
| 103 | 103 | my $compile_result = _run_command( $compile_command, "$test.cco", "$test.cco", $verbose ); | |||
| 104 | |||||
| 105 | 103 | if ($compile_result) { | |||
| 106 | 8 | confess "C compiler failed (see $test.cco)"; | |||
| 107 | 0 | return $compile_result; | |||
| 108 | } | ||||
| 109 | |||||
| 110 | 95 | my $link_result = | |||
| 111 | _run_command( "$link $linkflags $test$o $link_args ${cc_exe_out}${test}${exe} $libs", | ||||
| 112 | "$test.ldo", "$test.ldo", $verbose ) | ||||
| 113 | and confess "Linker failed (see $test.ldo)"; | ||||
| 114 | 95 | if ($link_result) { | |||
| 115 | 0 | return $link_result; | |||
| 116 | } | ||||
| 117 | } | ||||
| 118 | |||||
| 119 - 126 | =item C<cc_run()>
$conf->cc_run();
Calls the F<test> (or F<test.exe>) executable. Any output is directed to
F<test.out>.
=cut | ||||
| 127 | |||||
| 128 | sub cc_run { | ||||
| 129 | 87 | my $conf = shift; | |||
| 130 | 87 | my $exe = $conf->data->get('exe'); | |||
| 131 | 87 | my $slash = $conf->data->get('slash'); | |||
| 132 | 87 | my $verbose = $conf->options->get('verbose'); | |||
| 133 | 87 | my $test = 'test_' . $$; | |||
| 134 | 87 | my $test_exe = ".${slash}${test}${exe}"; | |||
| 135 | |||||
| 136 | 87 | my $run_error; | |||
| 137 | 87 | if ( defined( $_[0] ) && length( $_[0] ) ) { | |||
| 138 | 0 | local $" = ' '; | |||
| 139 | 0 | $run_error = _run_command( "$test_exe @_", "./$test.out", undef, $verbose ); | |||
| 140 | } | ||||
| 141 | else { | ||||
| 142 | 87 | $run_error = _run_command( $test_exe, "./$test.out", undef, $verbose ); | |||
| 143 | } | ||||
| 144 | |||||
| 145 | 87 | my $output = _slurp("./$test.out"); | |||
| 146 | |||||
| 147 | 87 | return $output; | |||
| 148 | } | ||||
| 149 | |||||
| 150 - 157 | =item C<cc_run_capture()>
$conf->cc_run_capture();
Same as C<cc_run()> except that warnings and errors are also directed to
F<test.out>.
=cut | ||||
| 158 | |||||
| 159 | sub cc_run_capture { | ||||
| 160 | 4 | my $conf = shift; | |||
| 161 | 4 | my $exe = $conf->data->get('exe'); | |||
| 162 | 4 | my $slash = $conf->data->get('slash'); | |||
| 163 | 4 | my $verbose = $conf->options->get('verbose'); | |||
| 164 | 4 | my $test = 'test_' . $$; | |||
| 165 | |||||
| 166 | 4 | if ( defined( $_[0] ) && length( $_[0] ) ) { | |||
| 167 | 0 | local $" = ' '; | |||
| 168 | 0 | _run_command( ".${slash}$test${exe} @_", "./$test.out", "./$test.out", $verbose ); | |||
| 169 | } | ||||
| 170 | else { | ||||
| 171 | 4 | _run_command( ".${slash}$test${exe}", "./$test.out", "./$test.out", $verbose ); | |||
| 172 | } | ||||
| 173 | |||||
| 174 | 4 | my $output = _slurp("./$test.out"); | |||
| 175 | |||||
| 176 | 4 | return $output; | |||
| 177 | } | ||||
| 178 | |||||
| 179 - 185 | =item C<cc_clean()>
$conf->cc_clean();
Cleans up all files in the root folder that match the glob F<test.*>.
=cut | ||||
| 186 | |||||
| 187 | sub cc_clean { ## no critic Subroutines::RequireFinalReturn | ||||
| 188 | 272 | my $conf = shift; | |||
| 189 | 272 | unlink map "test_${$}$_", qw( .c .cco .ldo .out ), | |||
| 190 | $conf->data->get(qw( o exe )), | ||||
| 191 | # MSVC | ||||
| 192 | qw( .exe.manifest .ilk .pdb ); | ||||
| 193 | } | ||||
| 194 | |||||
| 195 - 204 | =item C<shebang_mod()>
$conf->shebang_mod($source, $target);
Takes the specified source file, replacing entries like C<@key@> with
C<key>'s value from the configuration system's data, and writes the results
to specified target file. The replacement is only done in the first line of
the file normally to set the shebang value accordingly.
=cut | ||||
| 205 | |||||
| 206 | sub shebang_mod { | ||||
| 207 | 2 | my $conf = shift; | |||
| 208 | 2 | my ( $source, $target ) = @_; | |||
| 209 | |||||
| 210 | 2 | open my $in, '<', $source or die "Can't open $source: $!"; | |||
| 211 | 2 | open my $out, '>', "$target.tmp" or die "Can't open $target.tmp: $!"; | |||
| 212 | |||||
| 213 | 2 | my $line = <$in>; | |||
| 214 | |||||
| 215 | # interpolate @foo@ values | ||||
| 216 | 2 | $line =~ s{ \@ (\w+) \@ }{ | |||
| 217 | 2 | if(defined(my $val=$conf->data->get($1))) { | |||
| 218 | 2 | $val; | |||
| 219 | } | ||||
| 220 | else { | ||||
| 221 | 0 | warn "value for '\@$1\@' in $source is undef"; | |||
| 222 | 0 | ''; | |||
| 223 | } | ||||
| 224 | }egx; | ||||
| 225 | |||||
| 226 | 2 | print $out $line; | |||
| 227 | |||||
| 228 | 2 | while ( my $line = <$in> ) { | |||
| 229 | 1554 | print $out $line; | |||
| 230 | } | ||||
| 231 | |||||
| 232 | 2 | close($in) or die "Can't close $source: $!"; | |||
| 233 | 2 | close($out) or die "Can't close $target: $!"; | |||
| 234 | |||||
| 235 | 2 | move_if_diff( "$target.tmp", $target ); | |||
| 236 | } | ||||
| 237 | |||||
| 238 - 388 | =item C<genfile()>
$conf->genfile($source, $target, %options);
Takes the specified source file, replacing entries like C<@key@> with
C<key>'s value from the configuration system's data, and writes the results
to specified target file.
If a C<::> is present in the C<@key@>, the replaced value will first try to
use the full key, but if that is not present, the key up to the C<::> is used.
For example, if C<@cc_warn::src/embed.c@> is used, and that key doesn't
exist, the fallback key would be C<@cc_warn@>.
Respects the following options when manipulating files (Note: most of the
replacement syntax assumes the source text is on a single line.)
=over 4
=item file_type
If set to a C<makefile>, C<c> or C<perl> value, C<comment_type> will be set to
corresponding value. Moreover, when set to a C<makefile> value, it will
enable C<conditioned_lines>.
Its value will be detected automatically by target file name unless you set
it to a special value C<none>.
=item conditioned_lines #IF #UNLESS #ELSIF #ELSE
If conditioned_lines is true, then lines beginning in C<#IF>, C<#UNLESS>,
C<#ELSIF>, and C<#ELSE> are evaluated conditionally, and the content after the
C<:> is included or excluded, depending on the evaluation of the expression.
Lines beginning with C<#IF(expr):> are skipped if the expr condition is false,
otherwise the content after the C<:> is inserted. Lines beginning with
C<#UNLESS(expr):> are skipped if the expr condition is true, otherwise the
content after the C<:> is inserted. Lines beginning with C<#ELSIF(expr):> or
C<#ELSE:> are evaluated if the preceding C<#IF(expr):> evaluated to false.
A condition C<expr> may be:
=over 4
=item *
A single key, which is true if a config key is true,
=item *
Equal to the platform name or the osname - case-sensitive,
=item *
A C<key==value> expression, which is true if the config key has the expected
value, or
=item *
A logical combination of C<|>, C<OR>, C<&>, C<AND>, C<!>, C<NOT>.
=back
A key must only consist of the characters C<A-Z a-z 0-9 _ ->, and is checked
case-sensitively against the configuration key or the platform name. Truth is
defined as any value that is not C<0>, an empty string, or C<undef>.
The value in C<key==value> expressions may not contain spaces. Quotes in
values are not supported.
The word ops C<AND>, C<OR> and C<NOT> are case-insensitive. C<!> and C<NOT>
bind closer than C<&>, C<AND>, C<|>, and C<OR>. The order of precedence for
C<AND> and C<OR> is undefined.
For instance:
#IF(win32): src/atomic/gcc_x86$(O)
will be included if the platform is win32.
#IF(cpuarch==i386): src/atomic/gcc_x86$(O)
will be included if the value of the config key "cpuarch" is "i386".
#IF(cpuarch==i386): src/atomic/gcc_x86$(O)
#ELSIF(cpuarch==sparcv9): src/atomic/sparc_v9.s
#ELSE:
will include " src/atomic/gcc_x86$(O)" if the config key "cpuarch" is
ste to "i386", will include " src/atomic/sparc_v9.s" instead if
"cpuarch" is set to "sparcv9", and will include an empty line otherwise.
#IF(win32 and glut and not cygwin):
will be used on "win32" and if "glut" is defined, but not on "cygwin".
=item comment_type
This option takes has two possible values, C<#> or C</*>. If present and
set to one of these two values, the generated file will contain a
generated header that is commented out appropriately.
=item ignore_pattern
A regular expression. Any lines in the file matching this expression are
ignored when determining if the target file has changed (and should therefore
be overwritten with a new copy).
=item feature_file
When feature_file is set to a true value, a lines beginning with C<#perl>
forces the remaining lines of the file to be evaluated as perl code. Before
this evaluation occurs, any substitution of @@ values is performed on the
original text.
=item expand_gmake_syntax
If set to a true value, then certain types of I<gmake> syntax will be expanded
into their full equivalents. For example:
$(wildcard PATTERN)
Will be replaced B<at config time> with the list of files that match this
pattern. Note! Be very careful when determining whether or not to disable
this expansion during config time and letting I<gmake> evaluate these: the
config system itself may change state of the filesystem, causing the
directives to expand differently depending on when they're run. Another
potential issue to consider there is that most makefiles, while generated
from the root directory, are B<run> from a subdirectory. So relative path names
become an issue.
The I<gmake> replacements are done repeatedly on a single line, so nested
syntax works ok.
=over 4
=item addprefix
=item basename
=item wildcard
=item notdir
=back
=back
=back
=cut | ||||
| 389 | |||||
| 390 | sub genfile { | ||||
| 391 | 301 | my $conf = shift; | |||
| 392 | 301 | my ( $source, $target, %options ) = @_; | |||
| 393 | |||||
| 394 | 301 | my $calling_sub = (caller(1))[3] || q{}; | |||
| 395 | 301 | if ( $calling_sub !~ /cc_gen$/ ) { | |||
| 396 | 28 | $conf->append_configure_log($target); | |||
| 397 | } | ||||
| 398 | |||||
| 399 | 301 | open my $in, '<', $source or die "Can't open $source: $!"; | |||
| 400 | 300 | open my $out, '>', "$target.tmp" or die "Can't open $target.tmp: $!"; | |||
| 401 | |||||
| 402 | 300 | if ( !exists $options{file_type}) { | |||
| 403 | 23 | if ( $target =~ m/makefile$/i || $target =~ m/\.mak/) { | |||
| 404 | 9 | $options{file_type} = 'makefile'; | |||
| 405 | } | ||||
| 406 | elsif ($target =~ m/\.p[lm]$/i ) { | ||||
| 407 | 0 | $options{file_type} = 'perl'; | |||
| 408 | } | ||||
| 409 | elsif ($target =~ m/\.[hc]$/ ) { | ||||
| 410 | 3 | $options{file_type} = 'c'; | |||
| 411 | } | ||||
| 412 | elsif ($target =~ m/\.pmc$/ ) { | ||||
| 413 | 0 | $options{file_type} = 'pmc'; | |||
| 414 | } | ||||
| 415 | elsif ($target =~ m/\.pir$/ ) { | ||||
| 416 | 1 | $options{file_type} = 'pir'; | |||
| 417 | } | ||||
| 418 | } elsif ( $options{file_type} eq 'none' ) { | ||||
| 419 | 0 | delete $options{file_type}; | |||
| 420 | } | ||||
| 421 | |||||
| 422 | 300 | if ( $options{file_type} ) { | |||
| 423 | 290 | unless ( exists $file_types_info{$options{file_type}} ) { | |||
| 424 | 1 | die "Unknown file_type '$options{file_type}'"; | |||
| 425 | } | ||||
| 426 | 289 | unless ( exists $options{comment_type} ) { | |||
| 427 | 288 | $options{comment_type} = | |||
| 428 | $file_types_info{$options{file_type}}{comment_type}; | ||||
| 429 | } | ||||
| 430 | 289 | if ( $options{file_type} eq 'makefile' ) { | |||
| 431 | 12 | $options{conditioned_lines} = 1; | |||
| 432 | } | ||||
| 433 | } | ||||
| 434 | |||||
| 435 | 299 | if ( $options{comment_type} ) { | |||
| 436 | 289 | my @comment = ( 'ex: set ro:', | |||
| 437 | 'DO NOT EDIT THIS FILE', | ||||
| 438 | 'Generated by ' . __PACKAGE__ . " from $source" ); | ||||
| 439 | |||||
| 440 | 289 | if ( $options{comment_type} eq '#' ) { | |||
| 441 | 12 | foreach my $line (@comment) { | |||
| 442 | 36 | $line = "# $line\n"; | |||
| 443 | } | ||||
| 444 | } | ||||
| 445 | elsif ( $options{comment_type} eq '/*' ) { | ||||
| 446 | 276 | foreach my $line (@comment) { | |||
| 447 | 828 | $line = " * $line\n"; | |||
| 448 | } | ||||
| 449 | 276 | $comment[0] =~ s{^}{/*\n}; # '/*' | |||
| 450 | 276 | $comment[-1] =~ s{$}{\n */}; # ' */' | |||
| 451 | } | ||||
| 452 | else { | ||||
| 453 | 1 | die "Unknown comment type '$options{comment_type}'"; | |||
| 454 | } | ||||
| 455 | 288 288 | print {$out} @comment, "\n"; # extra newline after header | |||
| 456 | } | ||||
| 457 | |||||
| 458 | 298 | if ($target eq 'CFLAGS') { | |||
| 459 | 9 | $options{conditioned_lines} = 1; | |||
| 460 | } | ||||
| 461 | |||||
| 462 | # this loop can not be implemented as a foreach loop as the body | ||||
| 463 | # is dependent on <IN> being evaluated lazily | ||||
| 464 | |||||
| 465 | 298 | $conf->{_compiler_file} = $source; | |||
| 466 | 298 | my $former_truth = -1; | |||
| 467 | LINE: | ||||
| 468 | 298 | while ( my $line = <$in> ) { | |||
| 469 | 15064 | $conf->{_compiler_line} = $.; | |||
| 470 | |||||
| 471 | # everything after the line starting with #perl is eval'ed | ||||
| 472 | 15064 | if ( $line =~ /^#perl/ && $options{feature_file} ) { | |||
| 473 | |||||
| 474 | # OUT was/is used at the output filehandle in eval'ed scripts | ||||
| 475 | # e.g. feature.pl or feature_h.in | ||||
| 476 | 99 99 99 | no warnings 'once'; | |||
| 477 | 2 | local *OUT = $out; | |||
| 478 | 99 99 99 | use warnings; | |||
| 479 | 2 2 2 | my $text = do { local $/; <$in> }; | |||
| 480 | |||||
| 481 | # interpolate @foo@ values | ||||
| 482 | 2 | $text =~ s{ \@ (\w+) \@ }{\$conf->data->get("$1")}gx; | |||
| 483 | 2 | eval $text; | |||
| 484 | 2 | croak $@ if $@; | |||
| 485 | 1 | last LINE; | |||
| 486 | } | ||||
| 487 | 15062 | if ( $options{conditioned_lines} ) { | |||
| 488 | 7492 | my ($op, $expr, $rest); | |||
| 489 | # allow multiple keys and nested parens here | ||||
| 490 | 7492 | if (($op,$expr,$rest)=($line =~ m/^#(IF|UNLESS|ELSIF)\((.+)\):(.*)/s)) { | |||
| 491 | 219 | if (($op eq 'ELSIF') and $former_truth) { | |||
| 492 | 1 | next LINE; # no useless check if former IF was true | |||
| 493 | } | ||||
| 494 | 218 | my $truth = cond_eval($conf, $expr); | |||
| 495 | 217 | if ($op eq 'IF') { | |||
| 496 | 198 | $former_truth = $truth; | |||
| 497 | 198 | next LINE unless $truth; | |||
| 498 | } | ||||
| 499 | elsif ($op eq 'UNLESS') { | ||||
| 500 | 16 | $former_truth = !$truth; | |||
| 501 | 16 | next LINE if $truth; | |||
| 502 | } | ||||
| 503 | elsif ($op eq 'ELSIF') { | ||||
| 504 | 3 | $former_truth = $truth; | |||
| 505 | 3 | next LINE unless $truth; | |||
| 506 | } | ||||
| 507 | 84 | $line = $rest; | |||
| 508 | } | ||||
| 509 | elsif ( $former_truth != -1 and $line =~ m/^#ELSE:(.*)/s ) { | ||||
| 510 | 11 | next LINE if $former_truth; | |||
| 511 | 5 | $line = $1; | |||
| 512 | } | ||||
| 513 | else { # reset | ||||
| 514 | 7262 | $former_truth = -1; # ELSE must immediately follow a conditional. | |||
| 515 | } | ||||
| 516 | } | ||||
| 517 | |||||
| 518 | # interpolate gmake-ish expansions.. | ||||
| 519 | 14921 | if ( $options{expand_gmake_syntax} ) { | |||
| 520 | 4 | my $any_gmake; | |||
| 521 | 8 | GMAKES: | |||
| 522 | $any_gmake = 0; | ||||
| 523 | |||||
| 524 | 8 | if ( | |||
| 525 | $line =~ s{\$ \( wildcard \s+ ([^)]+) \)}{ | ||||
| 526 | 1 | join (' ', glob $1) | |||
| 527 | }egx | ||||
| 528 | ) | ||||
| 529 | { | ||||
| 530 | 1 | $any_gmake++; | |||
| 531 | } | ||||
| 532 | |||||
| 533 | 8 | if ( | |||
| 534 | $line =~ s{\$ \( notdir \s+ ([^)]+) \)}{ | ||||
| 535 | 3 | join (' ', | |||
| 536 | 1 | map { (File::Spec->splitpath($_))[2] } | |||
| 537 | split(' ', $1) | ||||
| 538 | ) | ||||
| 539 | }egx | ||||
| 540 | ) | ||||
| 541 | { | ||||
| 542 | 1 | $any_gmake++; | |||
| 543 | } | ||||
| 544 | |||||
| 545 | # documented as removing any .-based suffix | ||||
| 546 | 8 | if ( | |||
| 547 | $line =~ s{\$ \( basename \s+ ([^)]+) \)}{ | ||||
| 548 | 3 | join (' ', | |||
| 549 | map { | ||||
| 550 | 1 | my @split = File::Spec->splitpath($_); | |||
| 551 | 3 | $split[2] =~ s/\.[^.]*$//; | |||
| 552 | 3 | File::Spec->catpath(@split); | |||
| 553 | } split(' ', $1) | ||||
| 554 | ) | ||||
| 555 | }egx | ||||
| 556 | ) | ||||
| 557 | { | ||||
| 558 | 1 | $any_gmake++; | |||
| 559 | } | ||||
| 560 | |||||
| 561 | 8 | if ( | |||
| 562 | $line =~ s{\$ \( addprefix \s+ ([^,]+) \s* , \s* ([^)]+) \)}{ | ||||
| 563 | 1 | my ($prefix,$list) = ($1, $2); | |||
| 564 | 3 | join (' ', | |||
| 565 | 1 | map { $_ = $prefix . $_ } | |||
| 566 | split(' ', $list) | ||||
| 567 | ) | ||||
| 568 | }egx | ||||
| 569 | ) | ||||
| 570 | { | ||||
| 571 | 1 | $any_gmake++; | |||
| 572 | } | ||||
| 573 | |||||
| 574 | # we might have only gotten the innermost expression. try again. | ||||
| 575 | 8 | goto GMAKES if $any_gmake; | |||
| 576 | } | ||||
| 577 | |||||
| 578 | # interpolate @foo@ values | ||||
| 579 | 14921 | $line =~ s{ \@ (\w+) \@ }{ | |||
| 580 | 637 | if(defined(my $val=$conf->data->get($1))) { | |||
| 581 | 636 | $val; | |||
| 582 | } | ||||
| 583 | else { | ||||
| 584 | 1 | warn "value for '\@$1\@' in $source is undef"; | |||
| 585 | 1 | ''; | |||
| 586 | } | ||||
| 587 | }egx; | ||||
| 588 | |||||
| 589 | # interpolate @foo::bar@ values | ||||
| 590 | 14921 | $line =~ s{ \@ (\w+) :: ([^\@]+) \@ }{ | |||
| 591 | 30 | my $full = $1 . '::' . $2; | |||
| 592 | 30 | my $base = $1; | |||
| 593 | 30 | if(defined(my $val=$conf->data->get($full))) { | |||
| 594 | 9 | $val; | |||
| 595 | } | ||||
| 596 | elsif(defined($val=$conf->data->get($base))) { | ||||
| 597 | 21 | $val; | |||
| 598 | } | ||||
| 599 | else { | ||||
| 600 | 0 | warn "value for '\@$full\@' in $source is undef, no fallback"; | |||
| 601 | 0 | ''; | |||
| 602 | } | ||||
| 603 | }egx; | ||||
| 604 | |||||
| 605 | 14921 | print $out $line; | |||
| 606 | } | ||||
| 607 | |||||
| 608 | 296 | close($in) or die "Can't close $source: $!"; | |||
| 609 | 296 | close($out) or die "Can't close $target: $!"; | |||
| 610 | |||||
| 611 | 296 | move_if_diff( "$target.tmp", $target, $options{ignore_pattern} ); | |||
| 612 | } | ||||
| 613 | |||||
| 614 | # Return the next subexpression from the expression in $_[0] | ||||
| 615 | # and remove it from the input expression. | ||||
| 616 | # Allowed chars: A-Z a-z 0-9 _ -, so let's take [-\w]. | ||||
| 617 | # E.g. "(not win32 and has_glut)" | ||||
| 618 | # => not win32 => has_glut | ||||
| 619 | # "(!win32&has_glut)|cygwin" - perl-style | ||||
| 620 | # !win32&has_glut => !win32 => &has_glut => |cygwin | ||||
| 621 | sub next_expr { | ||||
| 622 | 345 | my $s = $_[0]; | |||
| 623 | 345 | return "" unless $s; | |||
| 624 | # start of a subexpression? | ||||
| 625 | 297 | if ($s =~ /^\((.+)\)\s*(.*)/) { # longest match to matching closing paren | |||
| 626 | 12 | $_[0] = $2 ? $2 : ""; # modify the 2nd arg | |||
| 627 | 12 | return $1; | |||
| 628 | } | ||||
| 629 | else { | ||||
| 630 | 285 | $s =~ s/^\s+//; # left-trim to make it more robust | |||
| 631 | 285 | if ($s =~ /^([-\w=]+)\s*(.*)?/) { # shortest match to next non-word char | |||
| 632 | # start with word expr | ||||
| 633 | 260 | $_[0] = $2 ? $2 : ""; # modify the 2nd arg expr in the caller | |||
| 634 | 260 | return $1; | |||
| 635 | } | ||||
| 636 | else { | ||||
| 637 | # special case: start with non-word op (perl-syntax only) | ||||
| 638 | 25 | $s =~ /^([|&!])\s*(.*)?/; # shortest match to next word char | |||
| 639 | 25 | $_[0] = $2 ? $2 : ""; # modify the 2nd arg expr in the caller | |||
| 640 | 25 | return $1; | |||
| 641 | } | ||||
| 642 | } | ||||
| 643 | } | ||||
| 644 | |||||
| 645 | # Checks the logical truth of the hash value: exists and not empty. | ||||
| 646 | # Also check the platform name, the 'osname' key, if the hash key does not exist. | ||||
| 647 | # Also check for key==value, like #IF(ld==gcc) | ||||
| 648 | sub cond_eval_single { | ||||
| 649 | 269 | my $conf = $_[0]; | |||
| 650 | 269 | my $key = $_[1]; | |||
| 651 | 269 | return unless defined $key; | |||
| 652 | 269 | if ($key =~ /^([-\w]+)==(.+)$/) { | |||
| 653 | 5 | return ($2 eq $conf->data->get($1)); | |||
| 654 | } | ||||
| 655 | else { | ||||
| 656 | 264 | return exists($conf->data->{c}->{$key}) | |||
| 657 | ? ($conf->data()->get($key) ? 1 : 0) | ||||
| 658 | : $key eq $conf->data()->get('osname'); | ||||
| 659 | } | ||||
| 660 | } | ||||
| 661 | |||||
| 662 | # Recursively evaluate boolean expressions with multiple keys and | & ! ops. | ||||
| 663 | # Order of precedence: Just "!" and "NOT" binds tighter than AND and OR. | ||||
| 664 | # There's no precedence for AND over OR defined, just left to right. | ||||
| 665 | sub cond_eval { | ||||
| 666 | 363 | my $conf = $_[0]; | |||
| 667 | 363 | my $expr = $_[1]; | |||
| 668 | 363 | my @count = split /[\s!&|\(]+/, $expr; # optimizable with tr | |||
| 669 | 363 | if (@count > 1) { # multiple keys: recurse into | |||
| 670 | 94 | my $truth = 0; | |||
| 671 | 94 | my $prevtruth = 0; | |||
| 672 | 94 | my $key = next_expr($expr); | |||
| 673 | 94 | my $op = ''; | |||
| 674 | LOOP: | ||||
| 675 | 94 | while ($key) { | |||
| 676 | 185 | if (($key eq '!') or (uc($key) eq 'NOT')) { | |||
| 677 | # bind next key immediately | ||||
| 678 | 20 | $op = 'NOT'; | |||
| 679 | 20 | $key = next_expr($expr); | |||
| 680 | } | ||||
| 681 | elsif ($truth and ($op eq 'OR')) { | ||||
| 682 | # true OR: => true | ||||
| 683 | 4 | last LOOP; | |||
| 684 | } | ||||
| 685 | 181 | $prevtruth = $truth; | |||
| 686 | 181 | if (!$truth and ($op eq 'AND')) { # false AND: => false, skip rest | |||
| 687 | 36 | last LOOP; | |||
| 688 | } | ||||
| 689 | 145 | $truth = cond_eval($conf, $key); | |||
| 690 | 145 | if ($op eq 'NOT') { # NOT *: invert | |||
| 691 | 20 | $truth = $truth ? 0 : 1; | |||
| 692 | } | ||||
| 693 | elsif ($op eq 'AND' and !$truth) { # * AND false: => false | ||||
| 694 | 5 | last LOOP; | |||
| 695 | } | ||||
| 696 | # * OR false => * (keep $truth). true OR * already handled before | ||||
| 697 | 140 | my $prevexpr = $expr; | |||
| 698 | 140 | $op = next_expr($expr); | |||
| 699 | 140 | if ($op) { | |||
| 700 | 92 | if ($op eq '|' or uc($op) eq 'OR') { | |||
| 701 | 29 | $op = 'OR'; | |||
| 702 | } | ||||
| 703 | elsif ($op eq '&' or uc($op) eq 'AND') { | ||||
| 704 | 62 | $op = 'AND'; | |||
| 705 | } | ||||
| 706 | elsif ($op eq '!' or uc($op) eq 'NOT') { | ||||
| 707 | 0 | $op = 'NOT'; | |||
| 708 | } | ||||
| 709 | else { | ||||
| 710 | 1 | makecroak($conf, "invalid op \"$op\" in \"$_[1]\" at \"$prevexpr\""); | |||
| 711 | } | ||||
| 712 | 91 | $key = next_expr($expr); | |||
| 713 | } | ||||
| 714 | elsif ($prevexpr) { | ||||
| 715 | 0 | makecroak($conf, "Makefile conditional syntax error: missing op in \"$_[1]\" at \"$prevexpr\""); | |||
| 716 | } | ||||
| 717 | else { | ||||
| 718 | 48 | last LOOP; # end of expr, nothing left | |||
| 719 | } | ||||
| 720 | 91 | if ($prevexpr eq $expr) { | |||
| 721 | 0 | makecroak($conf, "Makefile conditional parser error in \"$_[1]\" at \"$prevexpr\""); | |||
| 722 | } | ||||
| 723 | } | ||||
| 724 | 93 | return $truth; | |||
| 725 | } | ||||
| 726 | 269 | cond_eval_single($conf, $expr); | |||
| 727 | } | ||||
| 728 | |||||
| 729 | sub append_configure_log { | ||||
| 730 | 49 | my $conf = shift; | |||
| 731 | 49 | my $target = shift; | |||
| 732 | 49 | if ( $conf->{active_configuration} ) { | |||
| 733 | 24 | my $generated_log = 'MANIFEST.configure.generated'; | |||
| 734 | 24 | open my $GEN, '>>', $generated_log | |||
| 735 | or die "Can't open $generated_log for appending: $!"; | ||||
| 736 | 24 | print $GEN "$target\n"; | |||
| 737 | 24 | close $GEN or die "Can't close $generated_log after appending: $!"; | |||
| 738 | } | ||||
| 739 | } | ||||
| 740 | |||||
| 741 - 749 | =head1 SEE ALSO =over 4 =item F<docs/configuration.pod> =back =cut | ||||
| 750 | |||||
| 751 | 1; | ||||
| 752 | |||||
| 753 | # Local Variables: | ||||
| 754 | # mode: cperl | ||||
| 755 | # cperl-indent-level: 4 | ||||
| 756 | # fill-column: 100 | ||||
| 757 | # End: | ||||
| 758 | # vim: expandtab shiftwidth=4: | ||||