Blame lib/Autom4te/Channels.pm

Packit 47b4ca
# Copyright (C) 2002-2012 Free Software Foundation, Inc.
Packit 47b4ca
Packit 47b4ca
# This program is free software; you can redistribute it and/or modify
Packit 47b4ca
# it under the terms of the GNU General Public License as published by
Packit 47b4ca
# the Free Software Foundation; either version 2, or (at your option)
Packit 47b4ca
# any later version.
Packit 47b4ca
Packit 47b4ca
# This program is distributed in the hope that it will be useful,
Packit 47b4ca
# but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 47b4ca
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 47b4ca
# GNU General Public License for more details.
Packit 47b4ca
Packit 47b4ca
# You should have received a copy of the GNU General Public License
Packit 47b4ca
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
Packit 47b4ca
Packit 47b4ca
###############################################################
Packit 47b4ca
# The main copy of this file is in Automake's git repository. #
Packit 47b4ca
# Updates should be sent to automake-patches@gnu.org.         #
Packit 47b4ca
###############################################################
Packit 47b4ca
Packit 47b4ca
package Autom4te::Channels;
Packit 47b4ca
Packit 47b4ca
=head1 NAME
Packit 47b4ca
Packit 47b4ca
Autom4te::Channels - support functions for error and warning management
Packit 47b4ca
Packit 47b4ca
=head1 SYNOPSIS
Packit 47b4ca
Packit 47b4ca
  use Autom4te::Channels;
Packit 47b4ca
Packit 47b4ca
  # Register a channel to output warnings about unused variables.
Packit 47b4ca
  register_channel 'unused', type => 'warning';
Packit 47b4ca
Packit 47b4ca
  # Register a channel for system errors.
Packit 47b4ca
  register_channel 'system', type => 'error', exit_code => 4;
Packit 47b4ca
Packit 47b4ca
  # Output a message on channel 'unused'.
Packit 47b4ca
  msg 'unused', "$file:$line", "unused variable '$var'";
Packit 47b4ca
Packit 47b4ca
  # Make the 'unused' channel silent.
Packit 47b4ca
  setup_channel 'unused', silent => 1;
Packit 47b4ca
Packit 47b4ca
  # Turn on all channels of type 'warning'.
Packit 47b4ca
  setup_channel_type 'warning', silent => 0;
Packit 47b4ca
Packit 47b4ca
  # Redirect all channels to push messages on a Thread::Queue using
Packit 47b4ca
  # the specified serialization key.
Packit 47b4ca
  setup_channel_queue $queue, $key;
Packit 47b4ca
Packit 47b4ca
  # Output a message pending in a Thread::Queue.
Packit 47b4ca
  pop_channel_queue $queue;
Packit 47b4ca
Packit 47b4ca
  # Treat all warnings as errors.
Packit 47b4ca
  $warnings_are_errors = 1;
Packit 47b4ca
Packit 47b4ca
  # Exit with the greatest exit code encountered so far.
Packit 47b4ca
  exit $exit_code;
Packit 47b4ca
Packit 47b4ca
=head1 DESCRIPTION
Packit 47b4ca
Packit 47b4ca
This perl module provides support functions for handling diagnostic
Packit 47b4ca
channels in programs.  Channels can be registered to convey fatal,
Packit 47b4ca
error, warning, or debug messages.  Each channel has various options
Packit 47b4ca
(e.g. is the channel silent, should duplicate messages be removed,
Packit 47b4ca
etc.) that can also be overridden on a per-message basis.
Packit 47b4ca
Packit 47b4ca
=cut
Packit 47b4ca
Packit 47b4ca
use 5.006;
Packit 47b4ca
use strict;
Packit 47b4ca
use Exporter;
Packit 47b4ca
use Carp;
Packit 47b4ca
use File::Basename;
Packit 47b4ca
Packit 47b4ca
use vars qw (@ISA @EXPORT %channels $me);
Packit 47b4ca
Packit 47b4ca
@ISA = qw (Exporter);
Packit 47b4ca
@EXPORT = qw ($exit_code $warnings_are_errors
Packit 47b4ca
	      &reset_local_duplicates &reset_global_duplicates
Packit 47b4ca
	      &register_channel &msg &exists_channel &channel_type
Packit 47b4ca
	      &setup_channel &setup_channel_type
Packit 47b4ca
	      &dup_channel_setup &drop_channel_setup
Packit 47b4ca
	      &buffer_messages &flush_messages
Packit 47b4ca
	      &setup_channel_queue &pop_channel_queue
Packit 47b4ca
	      US_GLOBAL US_LOCAL
Packit 47b4ca
	      UP_NONE UP_TEXT UP_LOC_TEXT);
Packit 47b4ca
Packit 47b4ca
$me = basename $0;
Packit 47b4ca
Packit 47b4ca
=head2 Global Variables
Packit 47b4ca
Packit 47b4ca
=over 4
Packit 47b4ca
Packit 47b4ca
=item C<$exit_code>
Packit 47b4ca
Packit 47b4ca
The greatest exit code seen so far. C<$exit_code> is updated from
Packit 47b4ca
the C<exit_code> options of C<fatal> and C<error> channels.
Packit 47b4ca
Packit 47b4ca
=cut
Packit 47b4ca
Packit 47b4ca
use vars qw ($exit_code);
Packit 47b4ca
$exit_code = 0;
Packit 47b4ca
Packit 47b4ca
=item C<$warnings_are_errors>
Packit 47b4ca
Packit 47b4ca
Set this variable to 1 if warning messages should be treated as
Packit 47b4ca
errors (i.e. if they should update C<$exit_code>).
Packit 47b4ca
Packit 47b4ca
=cut
Packit 47b4ca
Packit 47b4ca
use vars qw ($warnings_are_errors);
Packit 47b4ca
$warnings_are_errors = 0;
Packit 47b4ca
Packit 47b4ca
=back
Packit 47b4ca
Packit 47b4ca
=head2 Constants
Packit 47b4ca
Packit 47b4ca
=over 4
Packit 47b4ca
Packit 47b4ca
=item C<UP_NONE>, C<UP_TEXT>, C<UP_LOC_TEXT>
Packit 47b4ca
Packit 47b4ca
Possible values for the C<uniq_part> options.  This selects the part
Packit 47b4ca
of the message that should be considered when filtering out duplicates.
Packit 47b4ca
If C<UP_LOC_TEXT> is used, the location and the explanation message
Packit 47b4ca
are used for filtering.  If C<UP_TEXT> is used, only the explanation
Packit 47b4ca
message is used (so the same message will be filtered out if it appears
Packit 47b4ca
at different locations).  C<UP_NONE> means that duplicate messages
Packit 47b4ca
should be output.
Packit 47b4ca
Packit 47b4ca
=cut
Packit 47b4ca
Packit 47b4ca
use constant UP_NONE => 0;
Packit 47b4ca
use constant UP_TEXT => 1;
Packit 47b4ca
use constant UP_LOC_TEXT => 2;
Packit 47b4ca
Packit 47b4ca
=item C<US_LOCAL>, C<US_GLOBAL>
Packit 47b4ca
Packit 47b4ca
Possible values for the C<uniq_scope> options.
Packit 47b4ca
Use C<US_GLOBAL> for error messages that should be printed only
Packit 47b4ca
once during the execution of the program, C<US_LOCAL> for message that
Packit 47b4ca
should be printed only once per file.  (Actually, C<Channels> does not
Packit 47b4ca
do this now when files are changed, it relies on you calling
Packit 47b4ca
C<reset_local_duplicates> when this happens.)
Packit 47b4ca
Packit 47b4ca
=cut
Packit 47b4ca
Packit 47b4ca
# possible values for uniq_scope
Packit 47b4ca
use constant US_LOCAL => 0;
Packit 47b4ca
use constant US_GLOBAL => 1;
Packit 47b4ca
Packit 47b4ca
=back
Packit 47b4ca
Packit 47b4ca
=head2 Options
Packit 47b4ca
Packit 47b4ca
Channels accept the options described below.  These options can be
Packit 47b4ca
passed as a hash to the C<register_channel>, C<setup_channel>, and C<msg>
Packit 47b4ca
functions.  The possible keys, with their default value are:
Packit 47b4ca
Packit 47b4ca
=over
Packit 47b4ca
Packit 47b4ca
=item C<type =E<gt> 'warning'>
Packit 47b4ca
Packit 47b4ca
The type of the channel.  One of C<'debug'>, C<'warning'>, C<'error'>, or
Packit 47b4ca
C<'fatal'>.  Fatal messages abort the program when they are output.
Packit 47b4ca
Error messages update the exit status.  Debug and warning messages are
Packit 47b4ca
harmless, except that warnings are treated as errors if
Packit 47b4ca
C<$warnings_are_errors> is set.
Packit 47b4ca
Packit 47b4ca
=item C<exit_code =E<gt> 1>
Packit 47b4ca
Packit 47b4ca
The value to update C<$exit_code> with when a fatal or error message
Packit 47b4ca
is emitted.  C<$exit_code> is also updated for warnings output
Packit 47b4ca
when C<$warnings_are_errors> is set.
Packit 47b4ca
Packit 47b4ca
=item C<file =E<gt> \*STDERR>
Packit 47b4ca
Packit 47b4ca
The file where the error should be output.
Packit 47b4ca
Packit 47b4ca
=item C<silent =E<gt> 0>
Packit 47b4ca
Packit 47b4ca
Whether the channel should be silent.  Use this do disable a
Packit 47b4ca
category of warning, for instance.
Packit 47b4ca
Packit 47b4ca
=item C<ordered =E<gt> 1>
Packit 47b4ca
Packit 47b4ca
Whether, with multi-threaded execution, the message should be queued
Packit 47b4ca
for ordered output.
Packit 47b4ca
Packit 47b4ca
=item C<uniq_part =E<gt> UP_LOC_TEXT>
Packit 47b4ca
Packit 47b4ca
The part of the message subject to duplicate filtering.  See the
Packit 47b4ca
documentation for the C<UP_NONE>, C<UP_TEXT>, and C<UP_LOC_TEXT>
Packit 47b4ca
constants above.
Packit 47b4ca
Packit 47b4ca
C<uniq_part> can also be set to an arbitrary string that will be used
Packit 47b4ca
instead of the message when considering duplicates.
Packit 47b4ca
Packit 47b4ca
=item C<uniq_scope =E<gt> US_LOCAL>
Packit 47b4ca
Packit 47b4ca
The scope of duplicate filtering.  See the documentation for the
Packit 47b4ca
C<US_LOCAL>, and C<US_GLOBAL> constants above.
Packit 47b4ca
Packit 47b4ca
=item C<header =E<gt> ''>
Packit 47b4ca
Packit 47b4ca
A string to prepend to each message emitted through this channel.
Packit 47b4ca
With partial messages, only the first part will have C<header>
Packit 47b4ca
prepended.
Packit 47b4ca
Packit 47b4ca
=item C<footer =E<gt> ''>
Packit 47b4ca
Packit 47b4ca
A string to append to each message emitted through this channel.
Packit 47b4ca
With partial messages, only the final part will have C<footer>
Packit 47b4ca
appended.
Packit 47b4ca
Packit 47b4ca
=item C<backtrace =E<gt> 0>
Packit 47b4ca
Packit 47b4ca
Die with a stack backtrace after displaying the message.
Packit 47b4ca
Packit 47b4ca
=item C<partial =E<gt> 0>
Packit 47b4ca
Packit 47b4ca
When set, indicates a partial message that should
Packit 47b4ca
be output along with the next message with C<partial> unset.
Packit 47b4ca
Several partial messages can be stacked this way.
Packit 47b4ca
Packit 47b4ca
Duplicate filtering will apply to the I<global> message resulting from
Packit 47b4ca
all I<partial> messages, using the options from the last (non-partial)
Packit 47b4ca
message.  Linking associated messages is the main reason to use this
Packit 47b4ca
option.
Packit 47b4ca
Packit 47b4ca
For instance the following messages
Packit 47b4ca
Packit 47b4ca
  msg 'channel', 'foo:2', 'redefinition of A ...';
Packit 47b4ca
  msg 'channel', 'foo:1', '... A previously defined here';
Packit 47b4ca
  msg 'channel', 'foo:3', 'redefinition of A ...';
Packit 47b4ca
  msg 'channel', 'foo:1', '... A previously defined here';
Packit 47b4ca
Packit 47b4ca
will result in
Packit 47b4ca
Packit 47b4ca
 foo:2: redefinition of A ...
Packit 47b4ca
 foo:1: ... A previously defined here
Packit 47b4ca
 foo:3: redefinition of A ...
Packit 47b4ca
Packit 47b4ca
where the duplicate "I<... A previously defined here>" has been
Packit 47b4ca
filtered out.
Packit 47b4ca
Packit 47b4ca
Linking these messages using C<partial> as follows will prevent the
Packit 47b4ca
fourth message to disappear.
Packit 47b4ca
Packit 47b4ca
  msg 'channel', 'foo:2', 'redefinition of A ...', partial => 1;
Packit 47b4ca
  msg 'channel', 'foo:1', '... A previously defined here';
Packit 47b4ca
  msg 'channel', 'foo:3', 'redefinition of A ...', partial => 1;
Packit 47b4ca
  msg 'channel', 'foo:1', '... A previously defined here';
Packit 47b4ca
Packit 47b4ca
Note that because the stack of C<partial> messages is printed with the
Packit 47b4ca
first non-C<partial> message, most options of C<partial> messages will
Packit 47b4ca
be ignored.
Packit 47b4ca
Packit 47b4ca
=back
Packit 47b4ca
Packit 47b4ca
=cut
Packit 47b4ca
Packit 47b4ca
use vars qw (%_default_options %_global_duplicate_messages
Packit 47b4ca
	     %_local_duplicate_messages);
Packit 47b4ca
Packit 47b4ca
# Default options for a channel.
Packit 47b4ca
%_default_options =
Packit 47b4ca
  (
Packit 47b4ca
   type => 'warning',
Packit 47b4ca
   exit_code => 1,
Packit 47b4ca
   file => \*STDERR,
Packit 47b4ca
   silent => 0,
Packit 47b4ca
   ordered => 1,
Packit 47b4ca
   queue => 0,
Packit 47b4ca
   queue_key => undef,
Packit 47b4ca
   uniq_scope => US_LOCAL,
Packit 47b4ca
   uniq_part => UP_LOC_TEXT,
Packit 47b4ca
   header => '',
Packit 47b4ca
   footer => '',
Packit 47b4ca
   backtrace => 0,
Packit 47b4ca
   partial => 0,
Packit 47b4ca
   );
Packit 47b4ca
Packit 47b4ca
# Filled with output messages as keys, to detect duplicates.
Packit 47b4ca
# The value associated with each key is the number of occurrences
Packit 47b4ca
# filtered out.
Packit 47b4ca
%_local_duplicate_messages = ();
Packit 47b4ca
%_global_duplicate_messages = ();
Packit 47b4ca
Packit 47b4ca
sub _reset_duplicates (\%)
Packit 47b4ca
{
Packit 47b4ca
  my ($ref) = @_;
Packit 47b4ca
  my $dup = 0;
Packit 47b4ca
  foreach my $k (keys %$ref)
Packit 47b4ca
    {
Packit 47b4ca
      $dup += $ref->{$k};
Packit 47b4ca
    }
Packit 47b4ca
  %$ref = ();
Packit 47b4ca
  return $dup;
Packit 47b4ca
}
Packit 47b4ca
Packit 47b4ca
Packit 47b4ca
=head2 Functions
Packit 47b4ca
Packit 47b4ca
=over 4
Packit 47b4ca
Packit 47b4ca
=item C<reset_local_duplicates ()>
Packit 47b4ca
Packit 47b4ca
Reset local duplicate messages (see C<US_LOCAL>), and
Packit 47b4ca
return the number of messages that have been filtered out.
Packit 47b4ca
Packit 47b4ca
=cut
Packit 47b4ca
Packit 47b4ca
sub reset_local_duplicates ()
Packit 47b4ca
{
Packit 47b4ca
  return _reset_duplicates %_local_duplicate_messages;
Packit 47b4ca
}
Packit 47b4ca
Packit 47b4ca
=item C<reset_global_duplicates ()>
Packit 47b4ca
Packit 47b4ca
Reset local duplicate messages (see C<US_GLOBAL>), and
Packit 47b4ca
return the number of messages that have been filtered out.
Packit 47b4ca
Packit 47b4ca
=cut
Packit 47b4ca
Packit 47b4ca
sub reset_global_duplicates ()
Packit 47b4ca
{
Packit 47b4ca
  return _reset_duplicates %_global_duplicate_messages;
Packit 47b4ca
}
Packit 47b4ca
Packit 47b4ca
sub _merge_options (\%%)
Packit 47b4ca
{
Packit 47b4ca
  my ($hash, %options) = @_;
Packit 47b4ca
  local $_;
Packit 47b4ca
Packit 47b4ca
  foreach (keys %options)
Packit 47b4ca
    {
Packit 47b4ca
      if (exists $hash->{$_})
Packit 47b4ca
	{
Packit 47b4ca
	  $hash->{$_} = $options{$_}
Packit 47b4ca
	}
Packit 47b4ca
      else
Packit 47b4ca
	{
Packit 47b4ca
	  confess "unknown option '$_'";
Packit 47b4ca
	}
Packit 47b4ca
    }
Packit 47b4ca
  if ($hash->{'ordered'})
Packit 47b4ca
    {
Packit 47b4ca
      confess "fatal messages cannot be ordered"
Packit 47b4ca
	if $hash->{'type'} eq 'fatal';
Packit 47b4ca
      confess "backtrace cannot be output on ordered messages"
Packit 47b4ca
	if $hash->{'backtrace'};
Packit 47b4ca
    }
Packit 47b4ca
}
Packit 47b4ca
Packit 47b4ca
=item C<register_channel ($name, [%options])>
Packit 47b4ca
Packit 47b4ca
Declare channel C<$name>, and override the default options
Packit 47b4ca
with those listed in C<%options>.
Packit 47b4ca
Packit 47b4ca
=cut
Packit 47b4ca
Packit 47b4ca
sub register_channel ($;%)
Packit 47b4ca
{
Packit 47b4ca
  my ($name, %options) = @_;
Packit 47b4ca
  my %channel_opts = %_default_options;
Packit 47b4ca
  _merge_options %channel_opts, %options;
Packit 47b4ca
  $channels{$name} = \%channel_opts;
Packit 47b4ca
}
Packit 47b4ca
Packit 47b4ca
=item C<exists_channel ($name)>
Packit 47b4ca
Packit 47b4ca
Returns true iff channel C<$name> has been registered.
Packit 47b4ca
Packit 47b4ca
=cut
Packit 47b4ca
Packit 47b4ca
sub exists_channel ($)
Packit 47b4ca
{
Packit 47b4ca
  my ($name) = @_;
Packit 47b4ca
  return exists $channels{$name};
Packit 47b4ca
}
Packit 47b4ca
Packit 47b4ca
=item C<channel_type ($name)>
Packit 47b4ca
Packit 47b4ca
Returns the type of channel C<$name> if it has been registered.
Packit 47b4ca
Returns the empty string otherwise.
Packit 47b4ca
Packit 47b4ca
=cut
Packit 47b4ca
Packit 47b4ca
sub channel_type ($)
Packit 47b4ca
{
Packit 47b4ca
  my ($name) = @_;
Packit 47b4ca
  return $channels{$name}{'type'} if exists_channel $name;
Packit 47b4ca
  return '';
Packit 47b4ca
}
Packit 47b4ca
Packit 47b4ca
# _format_sub_message ($LEADER, $MESSAGE)
Packit 47b4ca
# ---------------------------------------
Packit 47b4ca
# Split $MESSAGE at new lines and add $LEADER to each line.
Packit 47b4ca
sub _format_sub_message ($$)
Packit 47b4ca
{
Packit 47b4ca
  my ($leader, $message) = @_;
Packit 47b4ca
  return $leader . join ("\n" . $leader, split ("\n", $message)) . "\n";
Packit 47b4ca
}
Packit 47b4ca
Packit 47b4ca
# Store partial messages here. (See the 'partial' option.)
Packit 47b4ca
use vars qw ($partial);
Packit 47b4ca
$partial = '';
Packit 47b4ca
Packit 47b4ca
# _format_message ($LOCATION, $MESSAGE, %OPTIONS)
Packit 47b4ca
# -----------------------------------------------
Packit 47b4ca
# Format the message.  Return a string ready to print.
Packit 47b4ca
sub _format_message ($$%)
Packit 47b4ca
{
Packit 47b4ca
  my ($location, $message, %opts) = @_;
Packit 47b4ca
  my $msg = ($partial eq '' ? $opts{'header'} : '') . $message
Packit 47b4ca
	    . ($opts{'partial'} ? '' : $opts{'footer'});
Packit 47b4ca
  if (ref $location)
Packit 47b4ca
    {
Packit 47b4ca
      # If $LOCATION is a reference, assume it's an instance of the
Packit 47b4ca
      # Autom4te::Location class and display contexts.
Packit 47b4ca
      my $loc = $location->get || $me;
Packit 47b4ca
      $msg = _format_sub_message ("$loc: ", $msg);
Packit 47b4ca
      for my $pair ($location->get_contexts)
Packit 47b4ca
	{
Packit 47b4ca
	  $msg .= _format_sub_message ($pair->[0] . ":   ", $pair->[1]);
Packit 47b4ca
	}
Packit 47b4ca
    }
Packit 47b4ca
  else
Packit 47b4ca
    {
Packit 47b4ca
      $location ||= $me;
Packit 47b4ca
      $msg = _format_sub_message ("$location: ", $msg);
Packit 47b4ca
    }
Packit 47b4ca
  return $msg;
Packit 47b4ca
}
Packit 47b4ca
Packit 47b4ca
# _enqueue ($QUEUE, $KEY, $UNIQ_SCOPE, $TO_FILTER, $MSG, $FILE)
Packit 47b4ca
# -------------------------------------------------------------
Packit 47b4ca
# Push message on a queue, to be processed by another thread.
Packit 47b4ca
sub _enqueue ($$$$$$)
Packit 47b4ca
{
Packit 47b4ca
  my ($queue, $key, $uniq_scope, $to_filter, $msg, $file) = @_;
Packit 47b4ca
  $queue->enqueue ($key, $msg, $to_filter, $uniq_scope);
Packit 47b4ca
  confess "message queuing works only for STDERR"
Packit 47b4ca
    if $file ne \*STDERR;
Packit 47b4ca
}
Packit 47b4ca
Packit 47b4ca
# _dequeue ($QUEUE)
Packit 47b4ca
# -----------------
Packit 47b4ca
# Pop a message from a queue, and print, similarly to how
Packit 47b4ca
# _print_message would do it.  Return 0 if the queue is
Packit 47b4ca
# empty.  Note that the key has already been dequeued.
Packit 47b4ca
sub _dequeue ($)
Packit 47b4ca
{
Packit 47b4ca
  my ($queue) = @_;
Packit 47b4ca
  my $msg = $queue->dequeue || return 0;
Packit 47b4ca
  my $to_filter = $queue->dequeue;
Packit 47b4ca
  my $uniq_scope = $queue->dequeue;
Packit 47b4ca
  my $file = \*STDERR;
Packit 47b4ca
Packit 47b4ca
  if ($to_filter ne '')
Packit 47b4ca
    {
Packit 47b4ca
      # Do we want local or global uniqueness?
Packit 47b4ca
      my $dups;
Packit 47b4ca
      if ($uniq_scope == US_LOCAL)
Packit 47b4ca
	{
Packit 47b4ca
	  $dups = \%_local_duplicate_messages;
Packit 47b4ca
	}
Packit 47b4ca
      elsif ($uniq_scope == US_GLOBAL)
Packit 47b4ca
	{
Packit 47b4ca
	  $dups = \%_global_duplicate_messages;
Packit 47b4ca
	}
Packit 47b4ca
      else
Packit 47b4ca
	{
Packit 47b4ca
	  confess "unknown value for uniq_scope: " . $uniq_scope;
Packit 47b4ca
	}
Packit 47b4ca
Packit 47b4ca
      # Update the hash of messages.
Packit 47b4ca
      if (exists $dups->{$to_filter})
Packit 47b4ca
	{
Packit 47b4ca
	  ++$dups->{$to_filter};
Packit 47b4ca
	  return 1;
Packit 47b4ca
	}
Packit 47b4ca
      else
Packit 47b4ca
	{
Packit 47b4ca
	  $dups->{$to_filter} = 0;
Packit 47b4ca
	}
Packit 47b4ca
    }
Packit 47b4ca
  print $file $msg;
Packit 47b4ca
  return 1;
Packit 47b4ca
}
Packit 47b4ca
Packit 47b4ca
Packit 47b4ca
# _print_message ($LOCATION, $MESSAGE, %OPTIONS)
Packit 47b4ca
# ----------------------------------------------
Packit 47b4ca
# Format the message, check duplicates, and print it.
Packit 47b4ca
sub _print_message ($$%)
Packit 47b4ca
{
Packit 47b4ca
  my ($location, $message, %opts) = @_;
Packit 47b4ca
Packit 47b4ca
  return 0 if ($opts{'silent'});
Packit 47b4ca
Packit 47b4ca
  my $msg = _format_message ($location, $message, %opts);
Packit 47b4ca
  if ($opts{'partial'})
Packit 47b4ca
    {
Packit 47b4ca
      # Incomplete message.  Store, don't print.
Packit 47b4ca
      $partial .= $msg;
Packit 47b4ca
      return;
Packit 47b4ca
    }
Packit 47b4ca
  else
Packit 47b4ca
    {
Packit 47b4ca
      # Prefix with any partial message send so far.
Packit 47b4ca
      $msg = $partial . $msg;
Packit 47b4ca
      $partial = '';
Packit 47b4ca
    }
Packit 47b4ca
Packit 47b4ca
  msg ('note', '', 'warnings are treated as errors', uniq_scope => US_GLOBAL)
Packit 47b4ca
    if ($opts{'type'} eq 'warning' && $warnings_are_errors);
Packit 47b4ca
Packit 47b4ca
  # Check for duplicate message if requested.
Packit 47b4ca
  my $to_filter;
Packit 47b4ca
  if ($opts{'uniq_part'} ne UP_NONE)
Packit 47b4ca
    {
Packit 47b4ca
      # Which part of the error should we match?
Packit 47b4ca
      if ($opts{'uniq_part'} eq UP_TEXT)
Packit 47b4ca
	{
Packit 47b4ca
	  $to_filter = $message;
Packit 47b4ca
	}
Packit 47b4ca
      elsif ($opts{'uniq_part'} eq UP_LOC_TEXT)
Packit 47b4ca
	{
Packit 47b4ca
	  $to_filter = $msg;
Packit 47b4ca
	}
Packit 47b4ca
      else
Packit 47b4ca
	{
Packit 47b4ca
	  $to_filter = $opts{'uniq_part'};
Packit 47b4ca
	}
Packit 47b4ca
Packit 47b4ca
      # Do we want local or global uniqueness?
Packit 47b4ca
      my $dups;
Packit 47b4ca
      if ($opts{'uniq_scope'} == US_LOCAL)
Packit 47b4ca
	{
Packit 47b4ca
	  $dups = \%_local_duplicate_messages;
Packit 47b4ca
	}
Packit 47b4ca
      elsif ($opts{'uniq_scope'} == US_GLOBAL)
Packit 47b4ca
	{
Packit 47b4ca
	  $dups = \%_global_duplicate_messages;
Packit 47b4ca
	}
Packit 47b4ca
      else
Packit 47b4ca
	{
Packit 47b4ca
	  confess "unknown value for uniq_scope: " . $opts{'uniq_scope'};
Packit 47b4ca
	}
Packit 47b4ca
Packit 47b4ca
      # Update the hash of messages.
Packit 47b4ca
      if (exists $dups->{$to_filter})
Packit 47b4ca
	{
Packit 47b4ca
	  ++$dups->{$to_filter};
Packit 47b4ca
	  return 0;
Packit 47b4ca
	}
Packit 47b4ca
      else
Packit 47b4ca
	{
Packit 47b4ca
	  $dups->{$to_filter} = 0;
Packit 47b4ca
	}
Packit 47b4ca
    }
Packit 47b4ca
  my $file = $opts{'file'};
Packit 47b4ca
  if ($opts{'ordered'} && $opts{'queue'})
Packit 47b4ca
    {
Packit 47b4ca
      _enqueue ($opts{'queue'}, $opts{'queue_key'}, $opts{'uniq_scope'},
Packit 47b4ca
		$to_filter, $msg, $file);
Packit 47b4ca
    }
Packit 47b4ca
  else
Packit 47b4ca
    {
Packit 47b4ca
      print $file $msg;
Packit 47b4ca
    }
Packit 47b4ca
  return 1;
Packit 47b4ca
}
Packit 47b4ca
Packit 47b4ca
=item C<msg ($channel, $location, $message, [%options])>
Packit 47b4ca
Packit 47b4ca
Emit a message on C<$channel>, overriding some options of the channel with
Packit 47b4ca
those specified in C<%options>.  Obviously C<$channel> must have been
Packit 47b4ca
registered with C<register_channel>.
Packit 47b4ca
Packit 47b4ca
C<$message> is the text of the message, and C<$location> is a location
Packit 47b4ca
associated to the message.
Packit 47b4ca
Packit 47b4ca
For instance to complain about some unused variable C<mumble>
Packit 47b4ca
declared at line 10 in F<foo.c>, one could do:
Packit 47b4ca
Packit 47b4ca
  msg 'unused', 'foo.c:10', "unused variable 'mumble'";
Packit 47b4ca
Packit 47b4ca
If channel C<unused> is not silent (and if this message is not a duplicate),
Packit 47b4ca
the following would be output:
Packit 47b4ca
Packit 47b4ca
  foo.c:10: unused variable 'mumble'
Packit 47b4ca
Packit 47b4ca
C<$location> can also be an instance of C<Autom4te::Location>.  In this
Packit 47b4ca
case, the stack of contexts will be displayed in addition.
Packit 47b4ca
Packit 47b4ca
If C<$message> contains newline characters, C<$location> is prepended
Packit 47b4ca
to each line.  For instance,
Packit 47b4ca
Packit 47b4ca
  msg 'error', 'somewhere', "1st line\n2nd line";
Packit 47b4ca
Packit 47b4ca
becomes
Packit 47b4ca
Packit 47b4ca
  somewhere: 1st line
Packit 47b4ca
  somewhere: 2nd line
Packit 47b4ca
Packit 47b4ca
If C<$location> is an empty string, it is replaced by the name of the
Packit 47b4ca
program.  Actually, if you don't use C<%options>, you can even
Packit 47b4ca
elide the empty C<$location>.  Thus
Packit 47b4ca
Packit 47b4ca
  msg 'fatal', '', 'fatal error';
Packit 47b4ca
  msg 'fatal', 'fatal error';
Packit 47b4ca
Packit 47b4ca
both print
Packit 47b4ca
Packit 47b4ca
  progname: fatal error
Packit 47b4ca
Packit 47b4ca
=cut
Packit 47b4ca
Packit 47b4ca
Packit 47b4ca
use vars qw (@backlog %buffering);
Packit 47b4ca
Packit 47b4ca
# See buffer_messages() and flush_messages() below.
Packit 47b4ca
%buffering = ();	# The map of channel types to buffer.
Packit 47b4ca
@backlog = ();		# The buffer of messages.
Packit 47b4ca
Packit 47b4ca
sub msg ($$;$%)
Packit 47b4ca
{
Packit 47b4ca
  my ($channel, $location, $message, %options) = @_;
Packit 47b4ca
Packit 47b4ca
  if (! defined $message)
Packit 47b4ca
    {
Packit 47b4ca
      $message = $location;
Packit 47b4ca
      $location = '';
Packit 47b4ca
    }
Packit 47b4ca
Packit 47b4ca
  confess "unknown channel $channel" unless exists $channels{$channel};
Packit 47b4ca
Packit 47b4ca
  my %opts = %{$channels{$channel}};
Packit 47b4ca
  _merge_options (%opts, %options);
Packit 47b4ca
Packit 47b4ca
  if (exists $buffering{$opts{'type'}})
Packit 47b4ca
    {
Packit 47b4ca
      push @backlog, [$channel, $location->clone, $message, %options];
Packit 47b4ca
      return;
Packit 47b4ca
    }
Packit 47b4ca
Packit 47b4ca
  # Print the message if needed.
Packit 47b4ca
  if (_print_message ($location, $message, %opts))
Packit 47b4ca
    {
Packit 47b4ca
      # Adjust exit status.
Packit 47b4ca
      if ($opts{'type'} eq 'error'
Packit 47b4ca
	  || $opts{'type'} eq 'fatal'
Packit 47b4ca
	  || ($opts{'type'} eq 'warning' && $warnings_are_errors))
Packit 47b4ca
	{
Packit 47b4ca
	  my $es = $opts{'exit_code'};
Packit 47b4ca
	  $exit_code = $es if $es > $exit_code;
Packit 47b4ca
	}
Packit 47b4ca
Packit 47b4ca
      # Die on fatal messages.
Packit 47b4ca
      confess if $opts{'backtrace'};
Packit 47b4ca
      if ($opts{'type'} eq 'fatal')
Packit 47b4ca
        {
Packit 47b4ca
	  # flush messages explicitly here, needed in worker threads.
Packit 47b4ca
	  STDERR->flush;
Packit 47b4ca
	  exit $exit_code;
Packit 47b4ca
	}
Packit 47b4ca
    }
Packit 47b4ca
}
Packit 47b4ca
Packit 47b4ca
Packit 47b4ca
=item C<setup_channel ($channel, %options)>
Packit 47b4ca
Packit 47b4ca
Override the options of C<$channel> with those specified by C<%options>.
Packit 47b4ca
Packit 47b4ca
=cut
Packit 47b4ca
Packit 47b4ca
sub setup_channel ($%)
Packit 47b4ca
{
Packit 47b4ca
  my ($name, %opts) = @_;
Packit 47b4ca
  confess "unknown channel $name" unless exists $channels{$name};
Packit 47b4ca
  _merge_options %{$channels{$name}}, %opts;
Packit 47b4ca
}
Packit 47b4ca
Packit 47b4ca
=item C<setup_channel_type ($type, %options)>
Packit 47b4ca
Packit 47b4ca
Override the options of any channel of type C<$type>
Packit 47b4ca
with those specified by C<%options>.
Packit 47b4ca
Packit 47b4ca
=cut
Packit 47b4ca
Packit 47b4ca
sub setup_channel_type ($%)
Packit 47b4ca
{
Packit 47b4ca
  my ($type, %opts) = @_;
Packit 47b4ca
  foreach my $channel (keys %channels)
Packit 47b4ca
    {
Packit 47b4ca
      setup_channel $channel, %opts
Packit 47b4ca
	if $channels{$channel}{'type'} eq $type;
Packit 47b4ca
    }
Packit 47b4ca
}
Packit 47b4ca
Packit 47b4ca
=item C<dup_channel_setup ()>, C<drop_channel_setup ()>
Packit 47b4ca
Packit 47b4ca
Sometimes it is necessary to make temporary modifications to channels.
Packit 47b4ca
For instance one may want to disable a warning while processing a
Packit 47b4ca
particular file, and then restore the initial setup.  These two
Packit 47b4ca
functions make it easy: C<dup_channel_setup ()> saves a copy of the
Packit 47b4ca
current configuration for later restoration by
Packit 47b4ca
C<drop_channel_setup ()>.
Packit 47b4ca
Packit 47b4ca
You can think of this as a stack of configurations whose first entry
Packit 47b4ca
is the active one.  C<dup_channel_setup ()> duplicates the first
Packit 47b4ca
entry, while C<drop_channel_setup ()> just deletes it.
Packit 47b4ca
Packit 47b4ca
=cut
Packit 47b4ca
Packit 47b4ca
use vars qw (@_saved_channels @_saved_werrors);
Packit 47b4ca
@_saved_channels = ();
Packit 47b4ca
@_saved_werrors = ();
Packit 47b4ca
Packit 47b4ca
sub dup_channel_setup ()
Packit 47b4ca
{
Packit 47b4ca
  my %channels_copy;
Packit 47b4ca
  foreach my $k1 (keys %channels)
Packit 47b4ca
    {
Packit 47b4ca
      $channels_copy{$k1} = {%{$channels{$k1}}};
Packit 47b4ca
    }
Packit 47b4ca
  push @_saved_channels, \%channels_copy;
Packit 47b4ca
  push @_saved_werrors, $warnings_are_errors;
Packit 47b4ca
}
Packit 47b4ca
Packit 47b4ca
sub drop_channel_setup ()
Packit 47b4ca
{
Packit 47b4ca
  my $saved = pop @_saved_channels;
Packit 47b4ca
  %channels = %$saved;
Packit 47b4ca
  $warnings_are_errors = pop @_saved_werrors;
Packit 47b4ca
}
Packit 47b4ca
Packit 47b4ca
=item C<buffer_messages (@types)>, C<flush_messages ()>
Packit 47b4ca
Packit 47b4ca
By default, when C<msg> is called, messages are processed immediately.
Packit 47b4ca
Packit 47b4ca
Sometimes it is necessary to delay the output of messages.
Packit 47b4ca
For instance you might want to make diagnostics before
Packit 47b4ca
channels have been completely configured.
Packit 47b4ca
Packit 47b4ca
After C<buffer_messages(@types)> has been called, messages sent with
Packit 47b4ca
C<msg> to a channel whose type is listed in C<@types> will be stored in a
Packit 47b4ca
list for later processing.
Packit 47b4ca
Packit 47b4ca
This backlog of messages is processed when C<flush_messages> is
Packit 47b4ca
called, with the current channel options (not the options in effect,
Packit 47b4ca
at the time of C<msg>).  So for instance, if some channel was silenced
Packit 47b4ca
in the meantime, messages to this channel will not be printed.
Packit 47b4ca
Packit 47b4ca
C<flush_messages> cancels the effect of C<buffer_messages>.  Following
Packit 47b4ca
calls to C<msg> are processed immediately as usual.
Packit 47b4ca
Packit 47b4ca
=cut
Packit 47b4ca
Packit 47b4ca
sub buffer_messages (@)
Packit 47b4ca
{
Packit 47b4ca
  foreach my $type (@_)
Packit 47b4ca
    {
Packit 47b4ca
      $buffering{$type} = 1;
Packit 47b4ca
    }
Packit 47b4ca
}
Packit 47b4ca
Packit 47b4ca
sub flush_messages ()
Packit 47b4ca
{
Packit 47b4ca
  %buffering = ();
Packit 47b4ca
  foreach my $args (@backlog)
Packit 47b4ca
    {
Packit 47b4ca
      &msg (@$args);
Packit 47b4ca
    }
Packit 47b4ca
  @backlog = ();
Packit 47b4ca
}
Packit 47b4ca
Packit 47b4ca
=item C<setup_channel_queue ($queue, $key)>
Packit 47b4ca
Packit 47b4ca
Set the queue to fill for each channel that is ordered,
Packit 47b4ca
and the key to use for serialization.
Packit 47b4ca
Packit 47b4ca
=cut
Packit 47b4ca
sub setup_channel_queue ($$)
Packit 47b4ca
{
Packit 47b4ca
  my ($queue, $key) = @_;
Packit 47b4ca
  foreach my $channel (keys %channels)
Packit 47b4ca
    {
Packit 47b4ca
      setup_channel $channel, queue => $queue, queue_key => $key
Packit 47b4ca
        if $channels{$channel}{'ordered'};
Packit 47b4ca
    }
Packit 47b4ca
}
Packit 47b4ca
Packit 47b4ca
=item C<pop_channel_queue ($queue)>
Packit 47b4ca
Packit 47b4ca
pop a message off the $queue; the key has already been popped.
Packit 47b4ca
Packit 47b4ca
=cut
Packit 47b4ca
sub pop_channel_queue ($)
Packit 47b4ca
{
Packit 47b4ca
  my ($queue) = @_;
Packit 47b4ca
  return _dequeue ($queue);
Packit 47b4ca
}
Packit 47b4ca
Packit 47b4ca
=back
Packit 47b4ca
Packit 47b4ca
=head1 SEE ALSO
Packit 47b4ca
Packit 47b4ca
L<Autom4te::Location>
Packit 47b4ca
Packit 47b4ca
=head1 HISTORY
Packit 47b4ca
Packit 47b4ca
Written by Alexandre Duret-Lutz E<lt>F<adl@gnu.org>E<gt>.
Packit 47b4ca
Packit 47b4ca
=cut
Packit 47b4ca
Packit 47b4ca
1;
Packit 47b4ca
Packit 47b4ca
### Setup "GNU" style for perl-mode and cperl-mode.
Packit 47b4ca
## Local Variables:
Packit 47b4ca
## perl-indent-level: 2
Packit 47b4ca
## perl-continued-statement-offset: 2
Packit 47b4ca
## perl-continued-brace-offset: 0
Packit 47b4ca
## perl-brace-offset: 0
Packit 47b4ca
## perl-brace-imaginary-offset: 0
Packit 47b4ca
## perl-label-offset: -2
Packit 47b4ca
## cperl-indent-level: 2
Packit 47b4ca
## cperl-brace-offset: 0
Packit 47b4ca
## cperl-continued-brace-offset: 0
Packit 47b4ca
## cperl-label-offset: -2
Packit 47b4ca
## cperl-extra-newline-before-brace: t
Packit 47b4ca
## cperl-merge-trailing-else: nil
Packit 47b4ca
## cperl-continued-statement-offset: 2
Packit 47b4ca
## End: