source: trunk/essentials/sys-devel/automake-1.7/lib/Automake/Channels.pm

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

automake 1.7.9

File size: 14.4 KB
Line 
1# Copyright (C) 2002 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., 59 Temple Place - Suite 330, Boston, MA
16# 02111-1307, USA.
17
18package Automake::Channels;
19
20=head1 NAME
21
22Automake::Channels - support functions for error and warning management
23
24=head1 SYNOPSIS
25
26 use Automake::Channels;
27
28 # Register a channel to output warnings about unused variables.
29 register_channel 'unused', type => 'warning';
30
31 # Register a channel for system errors.
32 register_channel 'system', type => 'error', exit_code => 4;
33
34 # Output a message on channel 'unused'.
35 msg 'unused', "$file:$line", "unused variable `$var'";
36
37 # Make the 'unused' channel silent.
38 setup_channel 'unused', silent => 1;
39
40 # Turn on all channels of type 'warning'.
41 setup_channel_type 'warning', silent => 0;
42
43 # Treat all warnings as errors.
44 $warnings_are_errors = 1;
45
46 # Exit with the greater exist code encountered so far.
47 exit $exit_code;
48
49=head1 DESCRIPTION
50
51This perl module provides support functions for handling diagnostic
52channels in programs. Channels can be registered to convey fatal,
53error, warning, or debug messages. Each channel has various options
54(e.g. is the channel silent, should duplicate messages be removed,
55etc.) that can also be overridden on a per-message basis.
56
57=cut
58
59use 5.005;
60use strict;
61use Exporter;
62use Carp;
63use File::Basename;
64
65use vars qw (@ISA @EXPORT %channels $me);
66
67@ISA = qw (Exporter);
68@EXPORT = qw ($exit_code $warnings_are_errors
69 &reset_local_duplicates &reset_global_duplicates
70 &register_channel &msg &exists_channel &channel_type
71 &setup_channel &setup_channel_type
72 &dup_channel_setup &drop_channel_setup
73 &buffer_messages &flush_messages
74 US_GLOBAL US_LOCAL
75 UP_NONE UP_TEXT UP_LOC_TEXT);
76
77$me = basename $0;
78
79=head2 Global Variables
80
81=over 4
82
83=item C<$exit_code>
84
85The greatest exit code seen so far. C<$exit_code> is updated from
86the C<exit_code> options of C<fatal> and C<error> channels.
87
88=cut
89
90use vars qw ($exit_code);
91$exit_code = 0;
92
93=item C<$warnings_are_errors>
94
95Set this variable to 1 if warning messages should be treated as
96errors (i.e. if they should update C<$exit_code>).
97
98=cut
99
100use vars qw ($warnings_are_errors);
101$warnings_are_errors = 0;
102
103=back
104
105=head2 Constants
106
107=over 4
108
109=item C<UP_NONE>, C<UP_TEXT>, C<UP_LOC_TEXT>
110
111Possible values for the C<uniq_part> options. This select the part
112of the message that should be considered when filtering out duplicates.
113If C<UP_LOC_TEXT> is used, the location and the explanation message
114are used for filtering. If C<UP_TEXT> is used, only the explanation
115message is used (so the same message will be filtered out if it appears
116at different locations). C<UP_NONE> means that duplicate messages
117should be output.
118
119=cut
120
121use constant UP_NONE => 0;
122use constant UP_TEXT => 1;
123use constant UP_LOC_TEXT => 2;
124
125=item C<US_LOCAL>, C<US_GLOBAL>
126
127Possible values for the C<uniq_scope> options.
128Use C<US_GLOBAL> for error messages that should be printed only
129once in the run of the program, C<US_LOCAL> for message that
130should be printed only once per file. (Actually, C<Channels> does not
131now when files are changed, it relies on you calling C<reset_local_duplicates>
132when this happens.)
133
134=cut
135
136# possible values for uniq_scope
137use constant US_LOCAL => 0;
138use constant US_GLOBAL => 1;
139
140=back
141
142=head2 Options
143
144Channels accept the options described below. These options can be
145passed as a hash to the C<register_channel>, C<setup_channel>, and C<msg>
146functions. The possible keys, with there default value are:
147
148=over
149
150=item C<type =E<gt> 'warning'>
151
152The type of the channel. One of C<'debug'>, C<'warning'>, C<'error'>, or
153C<'fatal'>. Fatal messages abort the program when they are output.
154Error messages update the exit status. Debug and warning messages are
155harmless, except that warnings can be treated as errors of
156C<$warnings_are_errors> is set.
157
158=item C<exit_code =E<gt> 1>
159
160The value to update C<$exit_code> with when a fatal or error message
161is emitted. C<$exit_code> is also updated for warnings output
162when @<$warnings_are_errors> is set.
163
164=item C<file =E<gt> \*STDERR>
165
166The file where the error should be output.
167
168=item C<silent =E<gt> 0>
169
170Whether the channel should be silent. Use this do disable a
171category of warning, for instance.
172
173=item C<uniq_part =E<gt> UP_LOC_TEXT>
174
175The part of the message subject to duplicate filtering. See the
176documentation for the C<UP_NONE>, C<UP_TEXT>, and C<UP_LOC_TEXT>
177constants above.
178
179=item C<uniq_scope =E<gt> US_LOCAL>
180
181The scope of duplicate filtering. See the documentation for the
182C<US_LOCAL>, and C<US_GLOBAL> constants above.
183
184=item C<header =E<gt> ''>
185
186A string to prepend to each message emitted through this channel.
187
188=item C<footer =E<gt> ''>
189
190A string to append to each message emitted through this channel.
191
192=item C<backtrace =E<gt> 0>
193
194Die with a stack backtrace after displaying the message.
195
196=back
197
198=cut
199
200use vars qw (%_default_options %_global_duplicate_messages
201 %_local_duplicate_messages);
202
203# Default options for a channel.
204%_default_options =
205 (
206 type => 'warning',
207 exit_code => 1,
208 file => \*STDERR,
209 silent => 0,
210 uniq_scope => US_LOCAL,
211 uniq_part => UP_LOC_TEXT,
212 header => '',
213 footer => '',
214 backtrace => 0,
215 );
216
217# Filled with output messages as keys, to detect duplicates.
218# The value associated with each key is the number of occurrences
219# filtered out.
220%_local_duplicate_messages = ();
221%_global_duplicate_messages = ();
222
223sub _reset_duplicates (\%)
224{
225 my ($ref) = @_;
226 my $dup = 0;
227 foreach my $k (keys %$ref)
228 {
229 $dup += $ref->{$k};
230 }
231 %$ref = ();
232 return $dup;
233}
234
235
236=head2 Functions
237
238=over 4
239
240=item C<reset_local_duplicates ()>
241
242Reset local duplicate messages (see C<US_LOCAL>), and
243return the number of messages that have been filtered out.
244
245=cut
246
247sub reset_local_duplicates ()
248{
249 return _reset_duplicates %_local_duplicate_messages;
250}
251
252=item C<reset_global_duplicates ()>
253
254Reset local duplicate messages (see C<US_GLOBAL>), and
255return the number of messages that have been filtered out.
256
257=cut
258
259sub reset_global_duplicates ()
260{
261 return _reset_duplicates %_global_duplicate_messages;
262}
263
264sub _merge_options (\%%)
265{
266 my ($hash, %options) = @_;
267 local $_;
268
269 foreach (keys %options)
270 {
271 if (exists $hash->{$_})
272 {
273 $hash->{$_} = $options{$_}
274 }
275 else
276 {
277 confess "unknown option `$_'";
278 }
279 }
280}
281
282=item C<register_channel ($name, [%options])>
283
284Declare channel C<$name>, and override the default options
285with those listed in C<%options>.
286
287=cut
288
289sub register_channel ($;%)
290{
291 my ($name, %options) = @_;
292 my %channel_opts = %_default_options;
293 _merge_options %channel_opts, %options;
294 $channels{$name} = \%channel_opts;
295}
296
297=item C<exists_channel ($name)>
298
299Returns true iff channel C<$name> has been registered.
300
301=cut
302
303sub exists_channel ($)
304{
305 my ($name) = @_;
306 return exists $channels{$name};
307}
308
309=item C<channel_type ($name)>
310
311Returns the type of channel C<$name> if it has been registered.
312Returns The empty string otherwise.
313
314=cut
315
316sub channel_type ($)
317{
318 my ($name) = @_;
319 return $channels{$name}{'type'} if exists_channel $name;
320 return '';
321}
322
323# _format_message ($LEADER, $MESSAGE)
324# -----------------------------------
325# Split $MESSAGE at newlines and add $LEADER to each line.
326sub _format_message ($$)
327{
328 my ($leader, $message) = @_;
329 return $leader . join ("\n" . $leader, split ("\n", $message)) . "\n";
330}
331
332# _print_message ($LOCATION, $MESSAGE, %OPTIONS)
333# ----------------------------------------------
334# Format the message, check duplicates, and print it.
335sub _print_message ($$%)
336{
337 my ($location, $message, %opts) = @_;
338
339 return 0 if ($opts{'silent'});
340
341 if ($location)
342 {
343 $location .= ': ';
344 }
345 else
346 {
347 $location = "$me: ";
348 }
349
350 my $msg = _format_message ($location,
351 $opts{'header'} . $message . $opts{'footer'});
352
353 # Check for duplicate message if requested.
354 if ($opts{'uniq_part'} != UP_NONE)
355 {
356 # Which part of the error should we match?
357 my $to_filter;
358 if ($opts{'uniq_part'} == UP_TEXT)
359 {
360 $to_filter = $message;
361 }
362 elsif ($opts{'uniq_part'} == UP_LOC_TEXT)
363 {
364 $to_filter = $msg;
365 }
366 else
367 {
368 confess "unknown value for uniq_part: " . $opts{'uniq_part'};
369 }
370
371 # Do we want local or global uniqueness?
372 my $dups;
373 if ($opts{'uniq_scope'} == US_LOCAL)
374 {
375 $dups = \%_local_duplicate_messages;
376 }
377 elsif ($opts{'uniq_scope'} == US_GLOBAL)
378 {
379 $dups = \%_global_duplicate_messages;
380 }
381 else
382 {
383 confess "unknown value for uniq_scope: " . $opts{'uniq_scope'};
384 }
385
386 # Update the hash of messages.
387 if (exists $dups->{$to_filter})
388 {
389 ++$dups->{$to_filter};
390 return 0;
391 }
392 else
393 {
394 $dups->{$to_filter} = 0;
395 }
396 }
397 my $file = $opts{'file'};
398 print $file $msg;
399 return 1;
400}
401
402=item C<msg ($channel, $location, $message, [%options])>
403
404Emit a message on C<$channel>, overriding some options of the channel with
405those specified in C<%options>. Obviously C<$channel> must have been
406registered with C<register_channel>.
407
408C<$message> is the text of the message, and C<$location> is a location
409associated to the message.
410
411For instance to complain about some unused variable C<mumble>
412declared at line 10 in F<foo.c>, one could do:
413
414 msg 'unused', 'foo.c:10', "unused variable `mumble'";
415
416If channel C<unused> is not silent (and if this message is not a duplicate),
417the following would be output:
418
419 foo.c:10: unused variable `mumble'
420
421If C<$message> contains newline characters, C<$location> is prepended
422to each line. For instance
423
424 msg 'error', 'somewhere', "1st line\n2nd line";
425
426becomes
427
428 somewhere: 1st line
429 somewhere: 2nd line
430
431If C<$location> is an empty string, it is replaced by the name of the
432program. Actually, if you don't use C<%options>, you can even
433elide the empty C<$location>. Thus
434
435 msg 'fatal', '', 'fatal error';
436 msg 'fatal', 'fatal error';
437
438both print
439
440 progname: fatal error
441
442=cut
443
444
445use vars qw (@backlog %buffering);
446
447# See buffer_messages() and flush_messages() below.
448%buffering = (); # The map of channel types to buffer.
449@backlog = (); # The buffer of messages.
450
451sub msg ($$;$%)
452{
453 my ($channel, $location, $message, %options) = @_;
454
455 if (! defined $message)
456 {
457 $message = $location;
458 $location = '';
459 }
460
461 confess "unknown channel $channel" unless exists $channels{$channel};
462
463 my %opts = %{$channels{$channel}};
464 _merge_options (%opts, %options);
465
466 if (exists $buffering{$opts{'type'}})
467 {
468 push @backlog, [@_];
469 return;
470 }
471
472 # Print the message if needed.
473 if (_print_message ($location, $message, %opts))
474 {
475 # Adjust exit status.
476 if ($opts{'type'} eq 'error'
477 || $opts{'type'} eq 'fatal'
478 || ($opts{'type'} eq 'warning' && $warnings_are_errors))
479 {
480 my $es = $opts{'exit_code'};
481 $exit_code = $es if $es > $exit_code;
482 }
483
484 # Die on fatal messages.
485 confess if $opts{'backtrace'};
486 exit $exit_code if $opts{'type'} eq 'fatal';
487 }
488}
489
490
491=item C<setup_channel ($channel, %options)>
492
493Override the options of C<$channel> with those specified by C<%options>.
494
495=cut
496
497sub setup_channel ($%)
498{
499 my ($name, %opts) = @_;
500 confess "channel $name doesn't exist" unless exists $channels{$name};
501 _merge_options %{$channels{$name}}, %opts;
502}
503
504=item C<setup_channel_type ($type, %options)>
505
506Override the options of any channel of type C<$type>
507with those specified by C<%options>.
508
509=cut
510
511sub setup_channel_type ($%)
512{
513 my ($type, %opts) = @_;
514 foreach my $channel (keys %channels)
515 {
516 setup_channel $channel, %opts
517 if $channels{$channel}{'type'} eq $type;
518 }
519}
520
521=item C<dup_channel_setup ()>, C<drop_channel_setup ()>
522
523Sometimes it is necessary to make temporary modifications to channels.
524For instance one may want to disable a warning while processing a
525particular file, and then restore the initial setup. These two
526functions make it easy: C<dup_channel_setup ()> saves a copy of the
527current configuration for later restoration by
528C<drop_channel_setup ()>.
529
530You can think of this as a stack of configurations whose first entry
531is the active one. C<dup_channel_setup ()> duplicates the first
532entry, while C<drop_channel_setup ()> just deletes it.
533
534=cut
535
536use vars qw (@_saved_channels);
537@_saved_channels = ();
538
539sub dup_channel_setup ()
540{
541 my %channels_copy;
542 foreach my $k1 (keys %channels)
543 {
544 $channels_copy{$k1} = {%{$channels{$k1}}};
545 }
546 push @_saved_channels, \%channels_copy;
547}
548
549sub drop_channel_setup ()
550{
551 my $saved = pop @_saved_channels;
552 %channels = %$saved;
553}
554
555=item C<buffer_messages (@types)>, C<flush_messages ()>
556
557By default, when C<msg> is called, messages are processed immediately.
558
559Sometimes it is necessary to delay the output of messages.
560For instance you might want to make diagnostics before
561channels have been completely configured.
562
563After C<buffer_messages(@types)> has been called, messages sent with
564C<msg> to a channel whose type is listed in C<@types> will be stored in a
565list for later processing.
566
567This backlog of messages is processed when C<flush_messages> is
568called, with the current channel options (not the options in effect,
569at the time of C<msg>). So for instance if some channel was silenced
570in the meantime, messages to this channels will not be print.
571
572C<flush_messages> cancels the effect of C<buffer_messages>. Following
573calls to C<msg> are processed immediately as usual.
574
575=cut
576
577sub buffer_messages (@)
578{
579 foreach my $type (@_)
580 {
581 $buffering{$type} = 1;
582 }
583}
584
585sub flush_messages ()
586{
587 %buffering = ();
588 foreach my $args (@backlog)
589 {
590 &msg (@$args);
591 }
592 @backlog = ();
593}
594
595=back
596
597=head1 HISTORY
598
599Written by Alexandre Duret-Lutz E<lt>F<adl@gnu.org>E<gt>.
600
601=cut
602
6031;
604
605### Setup "GNU" style for perl-mode and cperl-mode.
606## Local Variables:
607## perl-indent-level: 2
608## perl-continued-statement-offset: 2
609## perl-continued-brace-offset: 0
610## perl-brace-offset: 0
611## perl-brace-imaginary-offset: 0
612## perl-label-offset: -2
613## cperl-indent-level: 2
614## cperl-brace-offset: 0
615## cperl-continued-brace-offset: 0
616## cperl-label-offset: -2
617## cperl-extra-newline-before-brace: t
618## cperl-merge-trailing-else: nil
619## cperl-continued-statement-offset: 2
620## End:
Note: See TracBrowser for help on using the repository browser.