doc: update Vala documentation
[automake.git] / lib / Automake / Channels.pm
blobde6020a6e3301c1dd9c40a1aa179a45fe958553f
1 # Copyright (C) 2002-2024 Free Software Foundation, Inc.
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.
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.
13 # You should have received a copy of the GNU General Public License
14 # along with this program. If not, see <https://www.gnu.org/licenses/>.
16 ##################################################################
17 # The master copy of this file is in Automake's source repository.
18 # Please send updates to automake-patches@gnu.org.
19 ##################################################################
21 package Automake::Channels;
23 =head1 NAME
25 Automake::Channels - support functions for error and warning management
27 =head1 SYNOPSIS
29 use Automake::Channels;
31 # Register a channel to output warnings about unused variables.
32 register_channel 'unused', type => 'warning';
34 # Register a channel for system errors.
35 register_channel 'system', type => 'error', exit_code => 4;
37 # Output a message on channel 'unused'.
38 msg 'unused', "$file:$line", "unused variable '$var'";
40 # Make the 'unused' channel silent.
41 setup_channel 'unused', silent => 1;
43 # Turn on all channels of type 'warning'.
44 setup_channel_type 'warning', silent => 0;
46 # Redirect all channels to push messages on a Thread::Queue using
47 # the specified serialization key.
48 setup_channel_queue $queue, $key;
50 # Output a message pending in a Thread::Queue.
51 pop_channel_queue $queue;
53 # Treat all warnings as errors.
54 $warnings_are_errors = 1;
56 # Exit with the greatest exit code encountered so far.
57 exit $exit_code;
59 =head1 DESCRIPTION
61 This perl module provides support functions for handling diagnostic
62 channels in programs. Channels can be registered to convey fatal,
63 error, warning, or debug messages. Each channel has various options
64 (e.g. is the channel silent, should duplicate messages be removed,
65 etc.) that can also be overridden on a per-message basis.
67 =cut
69 use 5.006;
70 use strict;
71 use warnings FATAL => 'all';
73 use Carp;
74 use Exporter;
75 use File::Basename;
77 our @ISA = qw (Exporter);
78 our @EXPORT = qw ($exit_code $warnings_are_errors
79 &reset_local_duplicates &reset_global_duplicates
80 &register_channel &msg &exists_channel &channel_type
81 &setup_channel &setup_channel_type
82 &dup_channel_setup &drop_channel_setup
83 &buffer_messages &flush_messages
84 &setup_channel_queue &pop_channel_queue
85 US_GLOBAL US_LOCAL
86 UP_NONE UP_TEXT UP_LOC_TEXT);
88 our %channels;
89 our $me = basename $0;
91 =head2 Global Variables
93 =over 4
95 =item C<$exit_code>
97 The greatest exit code seen so far. C<$exit_code> is updated from
98 the C<exit_code> options of C<fatal> and C<error> channels.
100 =cut
102 our $exit_code = 0;
104 =item C<$warnings_are_errors>
106 Set this variable to 1 if warning messages should be treated as
107 errors (i.e. if they should update C<$exit_code>).
109 =cut
111 our $warnings_are_errors = 0;
113 =back
115 =head2 Constants
117 =over 4
119 =item C<UP_NONE>, C<UP_TEXT>, C<UP_LOC_TEXT>
121 Possible values for the C<uniq_part> options. This selects the part
122 of the message that should be considered when filtering out duplicates.
123 If C<UP_LOC_TEXT> is used, the location and the explanation message
124 are used for filtering. If C<UP_TEXT> is used, only the explanation
125 message is used (so the same message will be filtered out if it appears
126 at different locations). C<UP_NONE> means that duplicate messages
127 should be output.
129 =cut
131 use constant UP_NONE => 0;
132 use constant UP_TEXT => 1;
133 use constant UP_LOC_TEXT => 2;
135 =item C<US_LOCAL>, C<US_GLOBAL>
137 Possible values for the C<uniq_scope> options.
138 Use C<US_GLOBAL> for error messages that should be printed only
139 once during the execution of the program, C<US_LOCAL> for message that
140 should be printed only once per file. (Actually, C<Channels> does not
141 do this now when files are changed, it relies on you calling
142 C<reset_local_duplicates> when this happens.)
144 =cut
146 # possible values for uniq_scope
147 use constant US_LOCAL => 0;
148 use constant US_GLOBAL => 1;
150 =back
152 =head2 Options
154 Channels accept the options described below. These options can be
155 passed as a hash to the C<register_channel>, C<setup_channel>, and C<msg>
156 functions. The possible keys, with their default value are:
158 =over
160 =item C<type =E<gt> 'warning'>
162 The type of the channel. One of C<'debug'>, C<'warning'>, C<'error'>, or
163 C<'fatal'>. Fatal messages abort the program when they are output.
164 Error messages update the exit status. Debug and warning messages are
165 harmless, except that warnings are treated as errors if
166 C<$warnings_are_errors> is set.
168 =item C<exit_code =E<gt> 1>
170 The value to update C<$exit_code> with when a fatal or error message
171 is emitted. C<$exit_code> is also updated for warnings output
172 when C<$warnings_are_errors> is set.
174 =item C<file =E<gt> \*STDERR>
176 The file where the error should be output.
178 =item C<silent =E<gt> 0>
180 Whether the channel should be silent. Use this do disable a
181 category of warning, for instance.
183 =item C<ordered =E<gt> 1>
185 Whether, with multi-threaded execution, the message should be queued
186 for ordered output.
188 =item C<uniq_part =E<gt> UP_LOC_TEXT>
190 The part of the message subject to duplicate filtering. See the
191 documentation for the C<UP_NONE>, C<UP_TEXT>, and C<UP_LOC_TEXT>
192 constants above.
194 C<uniq_part> can also be set to an arbitrary string that will be used
195 instead of the message when considering duplicates.
197 =item C<uniq_scope =E<gt> US_LOCAL>
199 The scope of duplicate filtering. See the documentation for the
200 C<US_LOCAL>, and C<US_GLOBAL> constants above.
202 =item C<header =E<gt> ''>
204 A string to prepend to each message emitted through this channel.
205 With partial messages, only the first part will have C<header>
206 prepended.
208 =item C<footer =E<gt> ''>
210 A string to append to each message emitted through this channel.
211 With partial messages, only the final part will have C<footer>
212 appended.
214 =item C<backtrace =E<gt> 0>
216 Die with a stack backtrace after displaying the message.
218 =item C<partial =E<gt> 0>
220 When set, indicates a partial message that should
221 be output along with the next message with C<partial> unset.
222 Several partial messages can be stacked this way.
224 Duplicate filtering will apply to the I<global> message resulting from
225 all I<partial> messages, using the options from the last (non-partial)
226 message. Linking associated messages is the main reason to use this
227 option.
229 For instance the following messages
231 msg 'channel', 'foo:2', 'redefinition of A ...';
232 msg 'channel', 'foo:1', '... A previously defined here';
233 msg 'channel', 'foo:3', 'redefinition of A ...';
234 msg 'channel', 'foo:1', '... A previously defined here';
236 will result in
238 foo:2: redefinition of A ...
239 foo:1: ... A previously defined here
240 foo:3: redefinition of A ...
242 where the duplicate "I<... A previously defined here>" has been
243 filtered out.
245 Linking these messages using C<partial> as follows will prevent the
246 fourth message to disappear.
248 msg 'channel', 'foo:2', 'redefinition of A ...', partial => 1;
249 msg 'channel', 'foo:1', '... A previously defined here';
250 msg 'channel', 'foo:3', 'redefinition of A ...', partial => 1;
251 msg 'channel', 'foo:1', '... A previously defined here';
253 Note that because the stack of C<partial> messages is printed with the
254 first non-C<partial> message, most options of C<partial> messages will
255 be ignored.
257 =back
259 =cut
261 # Default options for a channel.
262 our %_default_options =
264 type => 'warning',
265 exit_code => 1,
266 file => \*STDERR,
267 silent => 0,
268 ordered => 1,
269 queue => 0,
270 queue_key => undef,
271 uniq_scope => US_LOCAL,
272 uniq_part => UP_LOC_TEXT,
273 header => '',
274 footer => '',
275 backtrace => 0,
276 partial => 0,
279 # Filled with output messages as keys, to detect duplicates.
280 # The value associated with each key is the number of occurrences
281 # filtered out.
282 our %_local_duplicate_messages = ();
283 our %_global_duplicate_messages = ();
285 sub _reset_duplicates (\%)
287 my ($ref) = @_;
288 my $dup = 0;
289 foreach my $k (keys %$ref)
291 $dup += $ref->{$k};
293 %$ref = ();
294 return $dup;
298 =head2 Functions
300 =over 4
302 =item C<reset_local_duplicates ()>
304 Reset local duplicate messages (see C<US_LOCAL>), and
305 return the number of messages that have been filtered out.
307 =cut
309 sub reset_local_duplicates ()
311 return _reset_duplicates %_local_duplicate_messages;
314 =item C<reset_global_duplicates ()>
316 Reset local duplicate messages (see C<US_GLOBAL>), and
317 return the number of messages that have been filtered out.
319 =cut
321 sub reset_global_duplicates ()
323 return _reset_duplicates %_global_duplicate_messages;
326 sub _merge_options (\%%)
328 my ($hash, %options) = @_;
329 local $_;
331 foreach (keys %options)
333 if (exists $hash->{$_})
335 $hash->{$_} = $options{$_}
337 else
339 confess "unknown option '$_'";
342 if ($hash->{'ordered'})
344 confess "fatal messages cannot be ordered"
345 if $hash->{'type'} eq 'fatal';
346 confess "backtrace cannot be output on ordered messages"
347 if $hash->{'backtrace'};
351 =item C<register_channel ($name, [%options])>
353 Declare channel C<$name>, and override the default options
354 with those listed in C<%options>.
356 =cut
358 sub register_channel ($;%)
360 my ($name, %options) = @_;
361 my %channel_opts = %_default_options;
362 _merge_options %channel_opts, %options;
363 $channels{$name} = \%channel_opts;
366 =item C<exists_channel ($name)>
368 Returns true iff channel C<$name> has been registered.
370 =cut
372 sub exists_channel ($)
374 my ($name) = @_;
375 return exists $channels{$name};
378 =item C<channel_type ($name)>
380 Returns the type of channel C<$name> if it has been registered.
381 Returns the empty string otherwise.
383 =cut
385 sub channel_type ($)
387 my ($name) = @_;
388 return $channels{$name}{'type'} if exists_channel $name;
389 return '';
392 # _format_sub_message ($LEADER, $MESSAGE)
393 # ---------------------------------------
394 # Split $MESSAGE at new lines and add $LEADER to each line.
395 sub _format_sub_message ($$)
397 my ($leader, $message) = @_;
398 return $leader . join ("\n" . $leader, split ("\n", $message)) . "\n";
401 # Store partial messages here. (See the 'partial' option.)
402 our $partial = '';
404 # _format_message ($LOCATION, $MESSAGE, %OPTIONS)
405 # -----------------------------------------------
406 # Format the message. Return a string ready to print.
407 sub _format_message ($$%)
409 my ($location, $message, %opts) = @_;
410 my $msg = ($partial eq '' ? $opts{'header'} : '') . $message
411 . ($opts{'partial'} ? '' : $opts{'footer'});
412 if (ref $location)
414 # If $LOCATION is a reference, assume it's an instance of the
415 # Automake::Location class and display contexts.
416 my $loc = $location->get || $me;
417 $msg = _format_sub_message ("$loc: ", $msg);
418 for my $pair ($location->get_contexts)
420 $msg .= _format_sub_message ($pair->[0] . ": ", $pair->[1]);
423 else
425 $location ||= $me;
426 $msg = _format_sub_message ("$location: ", $msg);
428 return $msg;
431 # _enqueue ($QUEUE, $KEY, $UNIQ_SCOPE, $TO_FILTER, $MSG, $FILE)
432 # -------------------------------------------------------------
433 # Push message on a queue, to be processed by another thread.
434 sub _enqueue ($$$$$$)
436 my ($queue, $key, $uniq_scope, $to_filter, $msg, $file) = @_;
437 $queue->enqueue ($key, $msg, $to_filter, $uniq_scope);
438 confess "message queuing works only for STDERR"
439 if $file ne \*STDERR;
442 # _dequeue ($QUEUE)
443 # -----------------
444 # Pop a message from a queue, and print, similarly to how
445 # _print_message would do it. Return 0 if the queue is
446 # empty. Note that the key has already been dequeued.
447 sub _dequeue ($)
449 my ($queue) = @_;
450 my $msg = $queue->dequeue || return 0;
451 my $to_filter = $queue->dequeue;
452 my $uniq_scope = $queue->dequeue;
453 my $file = \*STDERR;
455 if ($to_filter ne '')
457 # Do we want local or global uniqueness?
458 my $dups;
459 if ($uniq_scope == US_LOCAL)
461 $dups = \%_local_duplicate_messages;
463 elsif ($uniq_scope == US_GLOBAL)
465 $dups = \%_global_duplicate_messages;
467 else
469 confess "unknown value for uniq_scope: " . $uniq_scope;
472 # Update the hash of messages.
473 if (exists $dups->{$to_filter})
475 ++$dups->{$to_filter};
476 return 1;
478 else
480 $dups->{$to_filter} = 0;
483 print $file $msg;
484 return 1;
488 # _print_message ($LOCATION, $MESSAGE, %OPTIONS)
489 # ----------------------------------------------
490 # Format the message, check duplicates, and print it.
491 sub _print_message ($$%)
493 my ($location, $message, %opts) = @_;
495 return 0 if ($opts{'silent'});
497 my $msg = _format_message ($location, $message, %opts);
498 if ($opts{'partial'})
500 # Incomplete message. Store, don't print.
501 $partial .= $msg;
502 return;
504 else
506 # Prefix with any partial message send so far.
507 $msg = $partial . $msg;
508 $partial = '';
511 msg ('note', '', 'warnings are treated as errors', uniq_scope => US_GLOBAL)
512 if ($opts{'type'} eq 'warning' && $warnings_are_errors);
514 # Check for duplicate message if requested.
515 my $to_filter;
516 if ($opts{'uniq_part'} ne UP_NONE)
518 # Which part of the error should we match?
519 if ($opts{'uniq_part'} eq UP_TEXT)
521 $to_filter = $message;
523 elsif ($opts{'uniq_part'} eq UP_LOC_TEXT)
525 $to_filter = $msg;
527 else
529 $to_filter = $opts{'uniq_part'};
532 # Do we want local or global uniqueness?
533 my $dups;
534 if ($opts{'uniq_scope'} == US_LOCAL)
536 $dups = \%_local_duplicate_messages;
538 elsif ($opts{'uniq_scope'} == US_GLOBAL)
540 $dups = \%_global_duplicate_messages;
542 else
544 confess "unknown value for uniq_scope: " . $opts{'uniq_scope'};
547 # Update the hash of messages.
548 if (exists $dups->{$to_filter})
550 ++$dups->{$to_filter};
551 return 0;
553 else
555 $dups->{$to_filter} = 0;
558 my $file = $opts{'file'};
559 if ($opts{'ordered'} && $opts{'queue'})
561 _enqueue ($opts{'queue'}, $opts{'queue_key'}, $opts{'uniq_scope'},
562 $to_filter, $msg, $file);
564 else
566 print $file $msg;
568 return 1;
571 =item C<msg ($channel, $location, $message, [%options])>
573 Emit a message on C<$channel>, overriding some options of the channel with
574 those specified in C<%options>. Obviously C<$channel> must have been
575 registered with C<register_channel>.
577 C<$message> is the text of the message, and C<$location> is a location
578 associated to the message.
580 For instance to complain about some unused variable C<mumble>
581 declared at line 10 in F<foo.c>, one could do:
583 msg 'unused', 'foo.c:10', "unused variable 'mumble'";
585 If channel C<unused> is not silent (and if this message is not a duplicate),
586 the following would be output:
588 foo.c:10: unused variable 'mumble'
590 C<$location> can also be an instance of C<Automake::Location>. In this
591 case, the stack of contexts will be displayed in addition.
593 If C<$message> contains newline characters, C<$location> is prepended
594 to each line. For instance,
596 msg 'error', 'somewhere', "1st line\n2nd line";
598 becomes
600 somewhere: 1st line
601 somewhere: 2nd line
603 If C<$location> is an empty string, it is replaced by the name of the
604 program. Actually, if you don't use C<%options>, you can even
605 elide the empty C<$location>. Thus
607 msg 'fatal', '', 'fatal error';
608 msg 'fatal', 'fatal error';
610 both print
612 progname: fatal error
614 =cut
617 # See buffer_messages() and flush_messages() below.
618 our %buffering = (); # The map of channel types to buffer.
619 our @backlog = (); # The buffer of messages.
621 sub msg ($$;$%)
623 my ($channel, $location, $message, %options) = @_;
625 if (! defined $message)
627 $message = $location;
628 $location = '';
631 if (!exists $channels{$channel})
633 # This can happen as a result of e.g. m4_warn([nonsense], [message])
634 # so it should not crash.
635 report_bad_channel($channel, $location);
636 $channel = 'syntax';
639 my %opts = %{$channels{$channel}};
640 _merge_options (%opts, %options);
642 if (exists $buffering{$opts{'type'}})
644 push @backlog, [$channel, $location->clone, $message, %options];
645 return;
648 # Print the message if needed.
649 if (_print_message ($location, $message, %opts))
651 # Adjust exit status.
652 if ($opts{'type'} eq 'error'
653 || $opts{'type'} eq 'fatal'
654 || ($opts{'type'} eq 'warning' && $warnings_are_errors))
656 my $es = $opts{'exit_code'};
657 $exit_code = $es if $es > $exit_code;
660 # Die on fatal messages.
661 confess if $opts{'backtrace'};
662 if ($opts{'type'} eq 'fatal')
664 # flush messages explicitly here, needed in worker threads.
665 STDERR->flush;
666 exit $exit_code;
671 sub report_bad_channel ($$)
673 my ($channel, $location) = @_;
674 my $message;
675 my $report_as = 'error';
677 # quotemeta is both too aggressive (e.g. it escapes '-') and
678 # too generous (it turns control characters into \ + themselves,
679 # not into symbolic escapes).
680 my $q_channel = $channel;
681 $q_channel =~ s/(?=[\"\$\'\@\`\\])/\\/g;
682 $q_channel =~ s/([^\x20-\x7e])/sprintf('\\x%02X', ord $1)/eg;
683 $q_channel = '"' . $q_channel . '"';
685 if ($channel eq '' || $channel eq 'all')
687 # Prior to version 2.70, the Autoconf manual said it was valid to use
688 # "all" and the empty string as the category argument to m4_warn, so
689 # don't treat those cases as errors.
690 $report_as = 'obsolete';
691 $message = "use of $q_channel as a diagnostic category is obsolete\n";
692 $message .= "(see automake --help for a list of valid categories)";
694 elsif ($channel eq 'none'
695 || ($channel =~ /^no-/ && exists $channels{substr($channel, 3)}))
697 # Also recognize "none" and "no-[category]", as someone might have
698 # thought anything acceptable to -W is also acceptable to m4_warn.
699 # Note: m4_warn([error], [...]) does actually issue an error.
700 $message = "-W accepts $q_channel, but it is not a diagnostic category";
702 else
704 $message = "unknown diagnostic category " . $q_channel;
707 msg $report_as, $location, $message;
711 =item C<setup_channel ($channel, %options)>
713 Override the options of C<$channel> with those specified by C<%options>.
715 =cut
717 sub setup_channel ($%)
719 my ($name, %opts) = @_;
720 confess "unknown channel $name" unless exists $channels{$name};
721 _merge_options %{$channels{$name}}, %opts;
724 =item C<setup_channel_type ($type, %options)>
726 Override the options of any channel of type C<$type>
727 with those specified by C<%options>.
729 =cut
731 sub setup_channel_type ($%)
733 my ($type, %opts) = @_;
734 foreach my $channel (keys %channels)
736 setup_channel $channel, %opts
737 if $channels{$channel}{'type'} eq $type;
741 =item C<dup_channel_setup ()>, C<drop_channel_setup ()>
743 Sometimes it is necessary to make temporary modifications to channels.
744 For instance one may want to disable a warning while processing a
745 particular file, and then restore the initial setup. These two
746 functions make it easy: C<dup_channel_setup ()> saves a copy of the
747 current configuration for later restoration by
748 C<drop_channel_setup ()>.
750 You can think of this as a stack of configurations whose first entry
751 is the active one. C<dup_channel_setup ()> duplicates the first
752 entry, while C<drop_channel_setup ()> just deletes it.
754 =cut
756 our @_saved_channels = ();
757 our @_saved_werrors = ();
759 sub dup_channel_setup ()
761 my %channels_copy;
762 foreach my $k1 (keys %channels)
764 $channels_copy{$k1} = {%{$channels{$k1}}};
766 push @_saved_channels, \%channels_copy;
767 push @_saved_werrors, $warnings_are_errors;
770 sub drop_channel_setup ()
772 my $saved = pop @_saved_channels;
773 %channels = %$saved;
774 $warnings_are_errors = pop @_saved_werrors;
777 =item C<buffer_messages (@types)>, C<flush_messages ()>
779 By default, when C<msg> is called, messages are processed immediately.
781 Sometimes it is necessary to delay the output of messages.
782 For instance you might want to make diagnostics before
783 channels have been completely configured.
785 After C<buffer_messages(@types)> has been called, messages sent with
786 C<msg> to a channel whose type is listed in C<@types> will be stored in a
787 list for later processing.
789 This backlog of messages is processed when C<flush_messages> is
790 called, with the current channel options (not the options in effect,
791 at the time of C<msg>). So for instance, if some channel was silenced
792 in the meantime, messages to this channel will not be printed.
794 C<flush_messages> cancels the effect of C<buffer_messages>. Following
795 calls to C<msg> are processed immediately as usual.
797 =cut
799 sub buffer_messages (@)
801 foreach my $type (@_)
803 $buffering{$type} = 1;
807 sub flush_messages ()
809 %buffering = ();
810 foreach my $args (@backlog)
812 &msg (@$args);
814 @backlog = ();
817 =item C<setup_channel_queue ($queue, $key)>
819 Set the queue to fill for each channel that is ordered,
820 and the key to use for serialization.
822 =cut
823 sub setup_channel_queue ($$)
825 my ($queue, $key) = @_;
826 foreach my $channel (keys %channels)
828 setup_channel $channel, queue => $queue, queue_key => $key
829 if $channels{$channel}{'ordered'};
833 =item C<pop_channel_queue ($queue)>
835 pop a message off the $queue; the key has already been popped.
837 =cut
838 sub pop_channel_queue ($)
840 my ($queue) = @_;
841 return _dequeue ($queue);
844 =back
846 =head1 SEE ALSO
848 L<Automake::Location>
850 =head1 HISTORY
852 Written by Alexandre Duret-Lutz E<lt>F<adl@gnu.org>E<gt>.
854 =cut