| # Copyright (C) 2003 Free Software Foundation, Inc. | |
| # This program is free software; you can redistribute it and/or modify | |
| # it under the terms of the GNU General Public License as published by | |
| # the Free Software Foundation; either version 2, or (at your option) | |
| # any later version. | |
| # This program is distributed in the hope that it will be useful, | |
| # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| # GNU General Public License for more details. | |
| # You should have received a copy of the GNU General Public License | |
| # along with this program; if not, write to the Free Software | |
| # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA | |
| # 02111-1307, USA. | |
| package Automake::FileUtils; | |
| =head1 NAME | |
| Automake::FileUtils - handling files | |
| =head1 SYNOPSIS | |
| use Automake::FileUtils | |
| =head1 DESCRIPTION | |
| This perl module provides various general purpose file handling functions. | |
| =cut | |
| use strict; | |
| use Exporter; | |
| use File::stat; | |
| use IO::File; | |
| use Automake::Channels; | |
| use Automake::ChannelDefs; | |
| use vars qw (@ISA @EXPORT); | |
| @ISA = qw (Exporter); | |
| @EXPORT = qw (&contents | |
| &find_file &mtime | |
| &update_file &up_to_date_p | |
| &xsystem &xqx); | |
| =item C<find_file ($filename, @include)> | |
| Return the first path for a C<$filename> in the C<include>s. | |
| We match exactly the behavior of GNU M4: first look in the current | |
| directory (which includes the case of absolute file names), and, if | |
| the file is not absolute, just fail. Otherwise, look in C<@include>. | |
| If the file is flagged as optional (ends with C<?>), then return undef | |
| if absent, otherwise exit with error. | |
| =cut | |
| # $FILENAME | |
| # find_file ($FILENAME, @INCLUDE) | |
| # ------------------------------- | |
| sub find_file ($@) | |
| { | |
| use File::Spec; | |
| my ($filename, @include) = @_; | |
| my $optional = 0; | |
| $optional = 1 | |
| if $filename =~ s/\?$//; | |
| return File::Spec->canonpath ($filename) | |
| if -e $filename; | |
| if (File::Spec->file_name_is_absolute ($filename)) | |
| { | |
| fatal "$filename: no such file or directory" | |
| unless $optional; | |
| return undef; | |
| } | |
| foreach my $path (@include) | |
| { | |
| return File::Spec->canonpath (File::Spec->catfile ($path, $filename)) | |
| if -e File::Spec->catfile ($path, $filename) | |
| } | |
| fatal "$filename: no such file or directory" | |
| unless $optional; | |
| return undef; | |
| } | |
| =item C<mtime ($file)> | |
| Return the mtime of C<$file>. Missing files, or C<-> standing for | |
| C<STDIN> or C<STDOUT> are ``obsolete'', i.e., as old as possible. | |
| =cut | |
| # $MTIME | |
| # MTIME ($FILE) | |
| # ------------- | |
| sub mtime ($) | |
| { | |
| my ($file) = @_; | |
| return 0 | |
| if $file eq '-' || ! -f $file; | |
| my $stat = stat ($file) | |
| or fatal "cannot stat $file: $!"; | |
| return $stat->mtime; | |
| } | |
| =item C<update_file ($from, $to)> | |
| Rename C<$from> as C<$to>, preserving C<$to> timestamp if it has not | |
| changed. Recognize C<$to> = C<-> standing for C<STDIN>. C<$from> is | |
| always removed/renamed. | |
| =cut | |
| # &update_file ($FROM, $TO) | |
| # ------------------------- | |
| sub update_file ($$) | |
| { | |
| my ($from, $to) = @_; | |
| my $SIMPLE_BACKUP_SUFFIX = $ENV{'SIMPLE_BACKUP_SUFFIX'} || '~'; | |
| use File::Compare; | |
| use File::Copy; | |
| if ($to eq '-') | |
| { | |
| my $in = new IO::File ("$from"); | |
| my $out = new IO::File (">-"); | |
| while ($_ = $in->getline) | |
| { | |
| print $out $_; | |
| } | |
| $in->close; | |
| unlink ($from) || fatal "cannot remove $from: $!"; | |
| return; | |
| } | |
| if (-f "$to" && compare ("$from", "$to") == 0) | |
| { | |
| # File didn't change, so don't update its mod time. | |
| msg 'note', "`$to' is unchanged"; | |
| unlink ($from) | |
| or fatal "cannot remove $from: $!"; | |
| return | |
| } | |
| if (-f "$to") | |
| { | |
| # Back up and install the new one. | |
| move ("$to", "$to$SIMPLE_BACKUP_SUFFIX") | |
| or fatal "cannot backup $to: $!"; | |
| move ("$from", "$to") | |
| or fatal "cannot rename $from as $to: $!"; | |
| msg 'note', "`$to' is updated"; | |
| } | |
| else | |
| { | |
| move ("$from", "$to") | |
| or fatal "cannot rename $from as $to: $!"; | |
| msg 'note', "`$to' is created"; | |
| } | |
| } | |
| =item C<up_to_date_p ($file, @dep)> | |
| Is C<$file> more recent than C<@dep>? | |
| =cut | |
| # $BOOLEAN | |
| # &up_to_date_p ($FILE, @DEP) | |
| # --------------------------- | |
| sub up_to_date_p ($@) | |
| { | |
| my ($file, @dep) = @_; | |
| my $mtime = mtime ($file); | |
| foreach my $dep (@dep) | |
| { | |
| if ($mtime < mtime ($dep)) | |
| { | |
| verb "up_to_date ($file): outdated: $dep"; | |
| return 0; | |
| } | |
| } | |
| verb "up_to_date ($file): up to date"; | |
| return 1; | |
| } | |
| =item C<handle_exec_errors ($command)> | |
| Display an error message for C<$command>, based on the content of | |
| C<$?> and C<$!>. | |
| =cut | |
| # handle_exec_errors ($COMMAND) | |
| # ----------------------------- | |
| sub handle_exec_errors ($) | |
| { | |
| my ($command) = @_; | |
| $command = (split (' ', $command))[0]; | |
| if ($!) | |
| { | |
| fatal "failed to run $command: $!"; | |
| } | |
| else | |
| { | |
| use POSIX qw (WIFEXITED WEXITSTATUS WIFSIGNALED WTERMSIG); | |
| if (WIFEXITED ($?)) | |
| { | |
| my $status = WEXITSTATUS ($?); | |
| # Propagate exit codes. | |
| fatal ('', | |
| "$command failed with exit status: $status", | |
| exit_code => $status); | |
| } | |
| elsif (WIFSIGNALED ($?)) | |
| { | |
| my $signal = WTERMSIG ($?); | |
| fatal "$command terminated by signal: $signal"; | |
| } | |
| else | |
| { | |
| fatal "$command exited abnormally"; | |
| } | |
| } | |
| } | |
| =item C<xqx ($command)> | |
| Same as C<qx> (but in scalar context), but fails on errors. | |
| =cut | |
| # xqx ($COMMAND) | |
| # -------------- | |
| sub xqx ($) | |
| { | |
| my ($command) = @_; | |
| verb "running: $command"; | |
| $! = 0; | |
| my $res = `$command`; | |
| handle_exec_errors $command | |
| if $?; | |
| return $res; | |
| } | |
| =item C<xsystem ($command)> | |
| Same as C<system>, but fails on errors, and reports the C<$command> | |
| in verbose mode. | |
| =cut | |
| # xsystem ($COMMAND) | |
| # ------------------ | |
| sub xsystem ($) | |
| { | |
| my ($command) = @_; | |
| verb "running: $command"; | |
| $! = 0; | |
| handle_exec_errors $command | |
| if system $command; | |
| } | |
| =item C<contents ($filename)> | |
| Return the contents of c<$filename>. | |
| =cut | |
| # contents ($FILENAME) | |
| # -------------------- | |
| sub contents ($) | |
| { | |
| my ($file) = @_; | |
| verb "reading $file"; | |
| local $/; # Turn on slurp-mode. | |
| my $f = new Automake::XFile "< $file"; | |
| my $contents = $f->getline; | |
| $f->close; | |
| return $contents; | |
| } | |
| 1; # for require | |
| ### Setup "GNU" style for perl-mode and cperl-mode. | |
| ## Local Variables: | |
| ## perl-indent-level: 2 | |
| ## perl-continued-statement-offset: 2 | |
| ## perl-continued-brace-offset: 0 | |
| ## perl-brace-offset: 0 | |
| ## perl-brace-imaginary-offset: 0 | |
| ## perl-label-offset: -2 | |
| ## cperl-indent-level: 2 | |
| ## cperl-brace-offset: 0 | |
| ## cperl-continued-brace-offset: 0 | |
| ## cperl-label-offset: -2 | |
| ## cperl-extra-newline-before-brace: t | |
| ## cperl-merge-trailing-else: nil | |
| ## cperl-continued-statement-offset: 2 | |
| ## End: |