| File: | lib/Parrot/Configure/Utils.pm |
| Coverage: | 94.5% |
| line | stmt | bran | cond | sub | code |
|---|---|---|---|---|---|
| 1 | # Copyright (C) 2001-2008, Parrot Foundation. | ||||
| 2 | |||||
| 3 - 21 | =head1 NAME Parrot::Configure::Utils - Configuration Step Utilities =head1 DESCRIPTION The C<Parrot::Configure::Utils> module contains utility functions for use by the configuration step classes found under F<config/>. The subroutines found in this module do B<not> require the Parrot::Configure object as an argument. Those subroutines formerly found in this module which B<do> require the Parrot::Configure object as an argument have been moved into Parrot::Configure::Compiler. =head2 Functions =over 4 =cut | ||||
| 22 | |||||
| 23 | package Parrot::Configure::Utils; | ||||
| 24 | |||||
| 25 | 103 103 103 | use strict; | |||
| 26 | 103 103 103 | use warnings; | |||
| 27 | |||||
| 28 | 103 103 103 | use base qw( Exporter ); | |||
| 29 | |||||
| 30 | 103 103 103 | use Carp; | |||
| 31 | 103 103 103 | use File::Copy (); | |||
| 32 | 103 103 103 | use File::Spec; | |||
| 33 | 103 103 103 | use File::Which; | |||
| 34 | 103 103 103 | use lib ("lib"); | |||
| 35 | 103 103 103 | use Parrot::BuildUtil (); | |||
| 36 | our @EXPORT = (); | ||||
| 37 | our @EXPORT_OK = qw( | ||||
| 38 | prompt copy_if_diff move_if_diff integrate | ||||
| 39 | capture_output check_progs _slurp | ||||
| 40 | _run_command _build_compile_command | ||||
| 41 | print_to_cache read_from_cache | ||||
| 42 | ); | ||||
| 43 | our %EXPORT_TAGS = ( | ||||
| 44 | inter => [qw(prompt integrate)], | ||||
| 45 | auto => [ | ||||
| 46 | qw(capture_output check_progs) | ||||
| 47 | ], | ||||
| 48 | gen => [qw( copy_if_diff move_if_diff )], | ||||
| 49 | cache => [qw( print_to_cache read_from_cache ) ], | ||||
| 50 | ); | ||||
| 51 | |||||
| 52 - 57 | =item C<_run_command($command, $out, $err)> Runs the specified command. Output is directed to the file specified by C<$out>, warnings and errors are directed to the file specified by C<$err>. =cut | ||||
| 58 | |||||
| 59 | sub _run_command { | ||||
| 60 | 465 | my ( $command, $out, $err, $verbose ) = @_; | |||
| 61 | |||||
| 62 | 465 | if ($verbose) { | |||
| 63 | 45 | print "$command\n"; | |||
| 64 | } | ||||
| 65 | |||||
| 66 | # Mostly copied from Parrot::Test.pm | ||||
| 67 | 465 | foreach ( $out, $err ) { | |||
| 68 | 930 | $_ = File::Spec->devnull | |||
| 69 | if $_ and $_ eq '/dev/null'; | ||||
| 70 | } | ||||
| 71 | |||||
| 72 | 465 | if ( $out and $err and $out eq $err ) { | |||
| 73 | 371 | $err = "&STDOUT"; | |||
| 74 | } | ||||
| 75 | |||||
| 76 | # Save the old filehandles; we must not let them get closed. | ||||
| 77 | 465 | open my $OLDOUT, '>&', \*STDOUT or die "Can't save stdout" if $out; | |||
| 78 | 465 | open my $OLDERR, '>&', \*STDERR or die "Can't save stderr" if $err; | |||
| 79 | |||||
| 80 | 465 | open STDOUT, '>', $out or die "Can't redirect stdout" if $out; | |||
| 81 | |||||
| 82 | # See 'Obscure Open Tricks' in perlopentut | ||||
| 83 | 465 | open STDERR, ">$err" ## no critic InputOutput::ProhibitTwoArgOpen | |||
| 84 | or die "Can't redirect stderr" | ||||
| 85 | if $err; | ||||
| 86 | |||||
| 87 | 465 | system $command; | |||
| 88 | 465 | my $exit_code = $? >> 8; | |||
| 89 | |||||
| 90 | 465 | close STDOUT or die "Can't close stdout" if $out; | |||
| 91 | 465 | close STDERR or die "Can't close stderr" if $err; | |||
| 92 | |||||
| 93 | 465 | open STDOUT, '>&', $OLDOUT or die "Can't restore stdout" if $out; | |||
| 94 | 465 | open STDERR, '>&', $OLDERR or die "Can't restore stderr" if $err; | |||
| 95 | |||||
| 96 | 465 | if ($verbose) { | |||
| 97 | 45 | foreach ( $out, $err ) { | |||
| 98 | 90 | if ( ( defined($_) ) | |||
| 99 | && ( $_ ne File::Spec->devnull ) | ||||
| 100 | && ( !m/^&/ ) ) | ||||
| 101 | { | ||||
| 102 | 46 | open( my $verbose_handle, '<', $_ ); | |||
| 103 | 46 | print <$verbose_handle>; | |||
| 104 | 46 | close $verbose_handle; | |||
| 105 | } | ||||
| 106 | } | ||||
| 107 | } | ||||
| 108 | |||||
| 109 | 465 | return $exit_code; | |||
| 110 | } | ||||
| 111 | |||||
| 112 - 116 | =item C<_build_compile_command( $cc, $ccflags, $cc_args )> Constructs a command-line to do the compile. =cut | ||||
| 117 | |||||
| 118 | sub _build_compile_command { | ||||
| 119 | 271 | my ( $cc, $ccflags, $cc_args ) = @_; | |||
| 120 | 271 | $_ ||= '' for ( $cc, $ccflags, $cc_args ); | |||
| 121 | |||||
| 122 | 271 | return "$cc $ccflags $cc_args -I./include -c test_$$.c"; | |||
| 123 | } | ||||
| 124 | |||||
| 125 - 129 | =item C<integrate($orig, $new)> Integrates C<$new> into C<$orig>. Returns C<$orig> if C<$new> is undefined. =cut | ||||
| 130 | |||||
| 131 | sub integrate { | ||||
| 132 | 127 | my ( $orig, $new ) = @_; | |||
| 133 | |||||
| 134 | # Rather than sprinkling "if defined(...)", everywhere, | ||||
| 135 | # various inter::* steps (coded in config/inter/*.pm) permit simply | ||||
| 136 | # passing in potentially undefined strings. | ||||
| 137 | # In these instances, we simply pass back the original string without | ||||
| 138 | # generating a warning. | ||||
| 139 | 127 | return $orig unless defined $new; | |||
| 140 | |||||
| 141 | 63 | if ( $new =~ /\S/ ) { | |||
| 142 | 60 | $orig = $new; | |||
| 143 | } | ||||
| 144 | |||||
| 145 | 63 | return $orig; | |||
| 146 | } | ||||
| 147 | |||||
| 148 - 153 | =item C<prompt($message, $value)> Prints out "message [default] " and waits for the user's response. Returns the response, or the default if the user just hit C<ENTER>. =cut | ||||
| 154 | |||||
| 155 | sub prompt { | ||||
| 156 | 55 | my ( $message, $value ) = @_; | |||
| 157 | |||||
| 158 | 55 | print("$message [$value] "); | |||
| 159 | |||||
| 160 | 55 | chomp( my $input = <STDIN> ); | |||
| 161 | |||||
| 162 | 55 | if ($input) { | |||
| 163 | 53 | $value = $input; | |||
| 164 | } | ||||
| 165 | |||||
| 166 | 55 | return integrate( $value, $input ); | |||
| 167 | } | ||||
| 168 | |||||
| 169 - 176 | =item C<file_checksum($filename, $ignore_pattern)> Creates a checksum for the specified file. This is used to compare files. Any lines matching the regular expression specified by C<$ignore_pattern> are not included in the checksum. =cut | ||||
| 177 | |||||
| 178 | sub file_checksum { | ||||
| 179 | 62 | my ( $filename, $ignore_pattern ) = @_; | |||
| 180 | 62 | open( my $file, '<', $filename ) or die "Can't open $filename: $!"; | |||
| 181 | 61 | my $sum = 0; | |||
| 182 | 61 | while (<$file>) { | |||
| 183 | 46454 | next if defined($ignore_pattern) && /$ignore_pattern/; | |||
| 184 | 46451 | $sum += unpack( "%32C*", $_ ); | |||
| 185 | } | ||||
| 186 | 61 | close($file) or die "Can't close $filename: $!"; | |||
| 187 | 61 | return $sum; | |||
| 188 | } | ||||
| 189 | |||||
| 190 - 198 | =item C<copy_if_diff($from, $to, $ignore_pattern)> Copies the file specified by C<$from> to the location specified by C<$to> if its contents have changed. The regular expression specified by C<$ignore_pattern> is passed to C<file_checksum()> when comparing the files. =cut | ||||
| 199 | |||||
| 200 | sub copy_if_diff { | ||||
| 201 | 304 | my ( $from, $to, $ignore_pattern ) = @_; | |||
| 202 | |||||
| 203 | # Don't touch the file if it didn't change (avoid unnecessary rebuilds) | ||||
| 204 | 304 | if ( -r $to ) { | |||
| 205 | 29 | my $from_sum = file_checksum( $from, $ignore_pattern ); | |||
| 206 | 29 | my $to_sum = file_checksum( $to, $ignore_pattern ); | |||
| 207 | 29 | return if $from_sum == $to_sum; | |||
| 208 | } | ||||
| 209 | |||||
| 210 | 286 | File::Copy::copy( $from, $to ); | |||
| 211 | |||||
| 212 | # Make sure the timestamp is updated | ||||
| 213 | 286 | my $now = time; | |||
| 214 | 286 | utime $now, $now, $to; | |||
| 215 | |||||
| 216 | 286 | return 1; | |||
| 217 | } | ||||
| 218 | |||||
| 219 - 224 | =item C<move_if_diff($from, $to, $ignore_pattern)> Moves the file specified by C<$from> to the location specified by C<$to> if its contents have changed. =cut | ||||
| 225 | |||||
| 226 | sub move_if_diff { ## no critic Subroutines::RequireFinalReturn | ||||
| 227 | 302 | my ( $from, $to, $ignore_pattern ) = @_; | |||
| 228 | 302 | copy_if_diff( $from, $to, $ignore_pattern ); | |||
| 229 | 302 | unlink $from; | |||
| 230 | } | ||||
| 231 | |||||
| 232 - 238 | =item C<capture_output($command)> Executes the given command. The command's output (both stdout and stderr), and its return status is returned as a 3-tuple. B<STDERR> is redirected to F<test.err> during the execution, and deleted after the command's run. =cut | ||||
| 239 | |||||
| 240 | sub capture_output { | ||||
| 241 | 33 | my $command = join ' ', @_; | |||
| 242 | |||||
| 243 | # disable STDERR | ||||
| 244 | 33 | open my $OLDERR, '>&', \*STDERR; | |||
| 245 | 33 | open STDERR, '>', "test_$$.err"; | |||
| 246 | |||||
| 247 | 33 | my $output = `$command`; | |||
| 248 | 33 | my $retval = ( $? == -1 ) ? -1 : ( $? >> 8 ); | |||
| 249 | |||||
| 250 | # reenable STDERR | ||||
| 251 | 33 | close STDERR; | |||
| 252 | 33 | open STDERR, '>&', $OLDERR; | |||
| 253 | |||||
| 254 | # slurp stderr | ||||
| 255 | 33 | my $out_err = _slurp("./test_$$.err"); | |||
| 256 | |||||
| 257 | # cleanup | ||||
| 258 | 33 | unlink "test_$$.err"; | |||
| 259 | |||||
| 260 | 33 | return ( $output, $out_err, $retval ) if wantarray; | |||
| 261 | 21 | return $output; | |||
| 262 | } | ||||
| 263 | |||||
| 264 - 273 | =item C<check_progs([$programs])> Where C<$programs> may be either a scalar with the name of a single program or an array ref of programs to search the current C<PATH> for. The first matching program name is returned or C<undef> on failure. Note: this function only returns the name of the program and not its complete path. This function is similar to C<autoconf>'s C<AC_CHECK_PROGS> macro. =cut | ||||
| 274 | |||||
| 275 | sub check_progs { | ||||
| 276 | 19 | my ( $progs, $verbose ) = @_; | |||
| 277 | |||||
| 278 | 19 | $progs = [$progs] unless ref $progs eq 'ARRAY'; | |||
| 279 | |||||
| 280 | 19 | print "checking for program: ", join( " or ", @$progs ), "\n" if $verbose; | |||
| 281 | 19 | foreach my $prog (@$progs) { | |||
| 282 | 31 | my $util = $prog; | |||
| 283 | |||||
| 284 | # use the first word in the string to ignore any options | ||||
| 285 | 31 | ($util) = $util =~ /(\S+)/; | |||
| 286 | 31 | my $path = which($util); | |||
| 287 | |||||
| 288 | 31 | if ($verbose) { | |||
| 289 | 6 | print "$path is executable\n" if $path; | |||
| 290 | } | ||||
| 291 | |||||
| 292 | 31 | return $prog if $path; | |||
| 293 | } | ||||
| 294 | |||||
| 295 | 6 | return; | |||
| 296 | } | ||||
| 297 | |||||
| 298 - 305 | =item C<print_to_cache( $cachefile, $value )> Opens a handle to write to the file specified in the first argument. Prints the value specified in the second argument, followed by a newline. Typically, this will be a hidden file in the user's top-level parrot directory. Implicitly returns true value upon success; C<die>s otherwise. =cut | ||||
| 306 | |||||
| 307 | sub print_to_cache { | ||||
| 308 | 1 | my ($cache, $value) = @_; | |||
| 309 | 1 | open my $FH, ">", $cache | |||
| 310 | or die "Unable to open handle to $cache for writing: $!"; | ||||
| 311 | 1 1 | print {$FH} "$value\n"; | |||
| 312 | 1 | close $FH or die "Unable to close handle to $cache after writing: $!"; | |||
| 313 | } | ||||
| 314 | |||||
| 315 - 321 | =item C<read_from_cache( $cachefile )> Opens a handle to read from the file specified in the first argument. This is assumed to be a file consisting of a single string, optionally terminated with a newline. The string is returned. =cut | ||||
| 322 | |||||
| 323 | sub read_from_cache { | ||||
| 324 | 5 | my ($cache) = @_; | |||
| 325 | 5 | my $value; | |||
| 326 | 5 | open my $FH, '<', $cache | |||
| 327 | or die "Unable to open $cache for reading: $!"; | ||||
| 328 | 5 | chomp($value = <$FH>); | |||
| 329 | 5 | close $FH or die "Unable to close $cache after reading: $!"; | |||
| 330 | 5 | return $value; | |||
| 331 | } | ||||
| 332 | |||||
| 333 - 338 | =item C<_slurp($filename)> Slurps C<$filename> into memory and returns it as a string. This is just an alias for C<Parrot::BuildUtil::slurp_file>. =cut | ||||
| 339 | |||||
| 340 | *_slurp = \&Parrot::BuildUtil::slurp_file; | ||||
| 341 | |||||
| 342 | =back | ||||
| 343 | |||||
| 344 | =cut | ||||
| 345 | |||||
| 346 - 358 | =head1 SEE ALSO =over 4 =item C<Parrot::Configure::runsteps()> =item F<docs/configuration.pod> =item F<lib/Parrot/Configure/Compiler.pm> =back =cut | ||||
| 359 | |||||
| 360 | 1; | ||||
| 361 | |||||
| 362 | # Local Variables: | ||||
| 363 | # mode: cperl | ||||
| 364 | # cperl-indent-level: 4 | ||||
| 365 | # fill-column: 100 | ||||
| 366 | # End: | ||||
| 367 | # vim: expandtab shiftwidth=4: | ||||