source: trunk/essentials/sys-devel/autoconf/lib/Autom4te/FileUtils.pm

Last change on this file was 3114, checked in by bird, 18 years ago

More @unixroot.

File size: 8.8 KB
Line 
1# Copyright (C) 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
2
3# This program is free software; you can redistribute it and/or modify
4# it under the terms of the GNU General Public License as published by
5# the Free Software Foundation; either version 2, or (at your option)
6# any later version.
7
8# This program is distributed in the hope that it will be useful,
9# but WITHOUT ANY WARRANTY; without even the implied warranty of
10# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11# GNU General Public License for more details.
12
13# You should have received a copy of the GNU General Public License
14# along with this program; if not, write to the Free Software
15# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
16# 02110-1301, USA.
17
18###############################################################
19# The main copy of this file is in Automake's CVS repository. #
20# Updates should be sent to automake-patches@gnu.org. #
21###############################################################
22
23package Autom4te::FileUtils;
24
25=head1 NAME
26
27Autom4te::FileUtils - handling files
28
29=head1 SYNOPSIS
30
31 use Autom4te::FileUtils
32
33=head1 DESCRIPTION
34
35This perl module provides various general purpose file handling functions.
36
37=cut
38
39use strict;
40use Exporter;
41use File::stat;
42use IO::File;
43use Autom4te::Channels;
44use Autom4te::ChannelDefs;
45
46use vars qw (@ISA @EXPORT);
47
48@ISA = qw (Exporter);
49@EXPORT = qw (&contents
50 &find_file &mtime
51 &update_file &up_to_date_p
52 &xsystem &xqx &dir_has_case_matching_file &reset_dir_cache);
53
54
55=item C<find_file ($file_name, @include)>
56
57Return the first path for a C<$file_name> in the C<include>s.
58
59We match exactly the behavior of GNU M4: first look in the current
60directory (which includes the case of absolute file names), and then,
61if the file name is not absolute, look in C<@include>.
62
63If the file is flagged as optional (ends with C<?>), then return undef
64if absent, otherwise exit with error.
65
66=cut
67
68# $FILE_NAME
69# find_file ($FILE_NAME, @INCLUDE)
70# -------------------------------
71sub find_file ($@)
72{
73 use File::Spec;
74
75 my ($file_name, @include) = @_;
76 $file_name =~ s/\/\@unixroot/$ENV{'UNIXROOT'}/; # The EMX built perl doesn't grok @unixroot.
77 my $optional = 0;
78
79 $optional = 1
80 if $file_name =~ s/\?$//;
81
82 return File::Spec->canonpath ($file_name)
83 if -e $file_name;
84
85 if (!File::Spec->file_name_is_absolute ($file_name))
86 {
87 foreach my $path (@include)
88 {
89 $path =~ s/\/\@unixroot/$ENV{'UNIXROOT'}/; # The EMX built perl doesn't grok @unixroot.
90 return File::Spec->canonpath (File::Spec->catfile ($path, $file_name))
91 if -e File::Spec->catfile ($path, $file_name)
92 }
93 }
94
95 fatal "$file_name: no such file or directory"
96 unless $optional;
97 return undef;
98}
99
100=item C<mtime ($file)>
101
102Return the mtime of C<$file>. Missing files, or C<-> standing for
103C<STDIN> or C<STDOUT> are ``obsolete'', i.e., as old as possible.
104
105=cut
106
107# $MTIME
108# MTIME ($FILE)
109# -------------
110sub mtime ($)
111{
112 my ($file) = @_;
113
114 return 0
115 if $file eq '-' || ! -f $file;
116
117 my $stat = stat ($file)
118 or fatal "cannot stat $file: $!";
119
120 return $stat->mtime;
121}
122
123
124=item C<update_file ($from, $to, [$force])>
125
126Rename C<$from> as C<$to>, preserving C<$to> timestamp if it has not
127changed, unless C<$force> is true (defaults to false). Recognize
128C<$to> = C<-> standing for C<STDIN>. C<$from> is always
129removed/renamed.
130
131=cut
132
133# &update_file ($FROM, $TO; $FORCE)
134# ---------------------------------
135sub update_file ($$;$)
136{
137 my ($from, $to, $force) = @_;
138 $force = 0
139 unless defined $force;
140 my $SIMPLE_BACKUP_SUFFIX = $ENV{'SIMPLE_BACKUP_SUFFIX'} || '~';
141 use File::Compare;
142 use File::Copy;
143
144 if ($to eq '-')
145 {
146 my $in = new IO::File ("$from");
147 my $out = new IO::File (">-");
148 while ($_ = $in->getline)
149 {
150 print $out $_;
151 }
152 $in->close;
153 unlink ($from) || fatal "cannot remove $from: $!";
154 return;
155 }
156
157 if (!$force && -f "$to" && compare ("$from", "$to") == 0)
158 {
159 # File didn't change, so don't update its mod time.
160 msg 'note', "`$to' is unchanged";
161 unlink ($from)
162 or fatal "cannot remove $from: $!";
163 return
164 }
165
166 if (-f "$to")
167 {
168 # Back up and install the new one.
169 move ("$to", "$to$SIMPLE_BACKUP_SUFFIX")
170 or fatal "cannot backup $to: $!";
171 move ("$from", "$to")
172 or fatal "cannot rename $from as $to: $!";
173 msg 'note', "`$to' is updated";
174 }
175 else
176 {
177 move ("$from", "$to")
178 or fatal "cannot rename $from as $to: $!";
179 msg 'note', "`$to' is created";
180 }
181}
182
183
184=item C<up_to_date_p ($file, @dep)>
185
186Is C<$file> more recent than C<@dep>?
187
188=cut
189
190# $BOOLEAN
191# &up_to_date_p ($FILE, @DEP)
192# ---------------------------
193sub up_to_date_p ($@)
194{
195 my ($file, @dep) = @_;
196 my $mtime = mtime ($file);
197
198 foreach my $dep (@dep)
199 {
200 if ($mtime < mtime ($dep))
201 {
202 verb "up_to_date ($file): outdated: $dep";
203 return 0;
204 }
205 }
206
207 verb "up_to_date ($file): up to date";
208 return 1;
209}
210
211
212=item C<handle_exec_errors ($command, [$expected_exit_code = 0])>
213
214Display an error message for C<$command>, based on the content of
215C<$?> and C<$!>. Be quiet if the command exited normally
216with C<$expected_exit_code>.
217
218=cut
219
220sub handle_exec_errors ($;$)
221{
222 my ($command, $expected) = @_;
223 $expected = 0 unless defined $expected;
224
225 $command = (split (' ', $command))[0];
226 if ($!)
227 {
228 fatal "failed to run $command: $!";
229 }
230 else
231 {
232 use POSIX qw (WIFEXITED WEXITSTATUS WIFSIGNALED WTERMSIG);
233
234 if (WIFEXITED ($?))
235 {
236 my $status = WEXITSTATUS ($?);
237 # Propagate exit codes.
238 fatal ('',
239 "$command failed with exit status: $status",
240 exit_code => $status)
241 unless $status == $expected;
242 }
243 elsif (WIFSIGNALED ($?))
244 {
245 my $signal = WTERMSIG ($?);
246 fatal "$command terminated by signal: $signal";
247 }
248 else
249 {
250 fatal "$command exited abnormally";
251 }
252 }
253}
254
255=item C<xqx ($command)>
256
257Same as C<qx> (but in scalar context), but fails on errors.
258
259=cut
260
261# xqx ($COMMAND)
262# --------------
263sub xqx ($)
264{
265 my ($command) = @_;
266
267 verb "running: $command";
268
269 $! = 0;
270 my $res = `$command`;
271 handle_exec_errors $command
272 if $?;
273
274 return $res;
275}
276
277
278=item C<xsystem (@argv)>
279
280Same as C<system>, but fails on errors, and reports the C<@argv>
281in verbose mode.
282
283=cut
284
285sub xsystem (@)
286{
287 my (@command) = @_;
288
289 verb "running: @command";
290
291 $! = 0;
292 handle_exec_errors "@command"
293 if system @command;
294}
295
296
297=item C<contents ($file_name)>
298
299Return the contents of C<$file_name>.
300
301=cut
302
303# contents ($FILE_NAME)
304# ---------------------
305sub contents ($)
306{
307 my ($file) = @_;
308 verb "reading $file";
309 local $/; # Turn on slurp-mode.
310 my $f = new Autom4te::XFile "< $file";
311 my $contents = $f->getline;
312 $f->close;
313 return $contents;
314}
315
316
317=item C<dir_has_case_matching_file ($DIRNAME, $FILE_NAME)>
318
319Return true iff $DIR contains a file name that matches $FILE_NAME case
320insensitively.
321
322We need to be cautious on case-insensitive case-preserving file
323systems (e.g. Mac OS X's HFS+). On such systems C<-f 'Foo'> and C<-f
324'foO'> answer the same thing. Hence if a package distributes its own
325F<CHANGELOG> file, but has no F<ChangeLog> file, automake would still
326try to distribute F<ChangeLog> (because it thinks it exists) in
327addition to F<CHANGELOG>, although it is impossible for these two
328files to be in the same directory (the two file names designate the
329same file).
330
331=cut
332
333use vars '%_directory_cache';
334sub dir_has_case_matching_file ($$)
335{
336 # Note that print File::Spec->case_tolerant returns 0 even on MacOS
337 # X (with Perl v5.8.1-RC3 at least), so do not try to shortcut this
338 # function using that.
339
340 my ($dirname, $file_name) = @_;
341 return 0 unless -f "$dirname/$file_name";
342
343 # The file appears to exist, however it might be a mirage if the
344 # system is case insensitive. Let's browse the directory and check
345 # whether the file is really in. We maintain a cache of directories
346 # so Automake doesn't spend all its time reading the same directory
347 # again and again.
348 if (!exists $_directory_cache{$dirname})
349 {
350 error "failed to open directory `$dirname'"
351 unless opendir (DIR, $dirname);
352 $_directory_cache{$dirname} = { map { $_ => 1 } readdir (DIR) };
353 closedir (DIR);
354 }
355 return exists $_directory_cache{$dirname}{$file_name};
356}
357
358=item C<reset_dir_cache ($dirname)>
359
360Clear C<dir_has_case_matching_file>'s cache for C<$dirname>.
361
362=cut
363
364sub reset_dir_cache ($)
365{
366 delete $_directory_cache{$_[0]};
367}
368
3691; # for require
370
371### Setup "GNU" style for perl-mode and cperl-mode.
372## Local Variables:
373## perl-indent-level: 2
374## perl-continued-statement-offset: 2
375## perl-continued-brace-offset: 0
376## perl-brace-offset: 0
377## perl-brace-imaginary-offset: 0
378## perl-label-offset: -2
379## cperl-indent-level: 2
380## cperl-brace-offset: 0
381## cperl-continued-brace-offset: 0
382## cperl-label-offset: -2
383## cperl-extra-newline-before-brace: t
384## cperl-merge-trailing-else: nil
385## cperl-continued-statement-offset: 2
386## End:
Note: See TracBrowser for help on using the repository browser.