automake-patches
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

monstro-patch: machinery to enable/disable warning/error categories


From: Alexandre Duret-Lutz
Subject: monstro-patch: machinery to enable/disable warning/error categories
Date: 05 Jul 2002 19:14:23 +0200
User-agent: Gnus/5.0808 (Gnus v5.8.8) Emacs/20.7

Automake doesn't have "warnings", it can only print "errors"
that cannot be muted and cause a non-0 exit status.  Several
diagnosis have never been implemented because of this (e.g. we
don't warn if the user override CFLAGS instead of AM_CFLAGS in
it's Makefile, because reporting this as an `error' would hurt
many users).

This patch adds support for warning categories (-Wmumble).

The new file lib/Automake/Channels.pm contains the machinery
to handle messages channels.  The main interface is `msg'.
Once a channel is registered (let's say it's called 'unused'),
messages can be emitted with `msg':

  msg 'unused', "$file:$line", "unused variable `$var'";

The channel 'unused' can be configured to be a warning, or an
error (in which case it update the exit_status), it can be muted
(think -Wno-unused), it can be told to filter duplicate messages
(this simplify a few places in Automake where this was done
manually).  See the embeded POD for the other options.

The main purpose of these `channels' is to help enabling or
disabling warnings, but the same is also true for errors.
For instance in Automake the `error-gnu' and `error-gnits'
channels are disabled in `foreign' mode.  Doing this simplify
the code by avoiding `if ($stricness == GNU)' tests.

Since most errors fall into the 'error' category, I've defined
`err', an alias for `msg 'error','.  And because in many places
`$file:$line' is the location of a variable, a target,
Makefile.am, or configure.ac, we also have the following
shorthands:

   msg_var
   err_var          (replaces `macro_error')
   msg_target
   err_target       (replaces `target_error')
   msg_am
   err_am           (replaces `am_error')
   msg_ac
   err_ac           (replaces `conf_error')

(`err' itself can be seen as a replacement for `file_error')

The reason why the old `macro_error', `target_error', etc. have
been transformed into `err_var', `err_target', etc. is that
I've first rewritten all the code using only `msg' and got rid
of these functions.  Then I realized msg_var, msg_target,
msg_am, and msg_ac would be welcome, and finally that the
'error' channel was used the most and so it made sense to have
the `err*' aliases to save typing.

Also, I've found many places doing something like the following:

   err_var $var, "don't use $var"
      if defined $var;
   err_target $target, "don't use $target"
      if defined $target;

This becomes

   reject_var $var, "don't use $var";
   reject_target $target, "don't use $target";

Finally, Automake supported --Werror and --Wno-error options.
These are now -Werror and -Wno-error (although the old names are
still understood).  I've never really liked --Werror, because it
caused Automake to abort after the first error; this has
changed, -Werror will display all errors before aborting.

Comments?

Here are a few warnings that I'd like to implement after this.

  - warn for portability issues we know about (like variable
    names with leading underscore)

  - add more warning about obsolete features (e.g. INCLUDES
    is the old name for AM_CPPFLAGS)

  - warn if the Makefile.am authors is overriding a user 
    variable like CFLAGS.

2002-07-05  Alexandre Duret-Lutz  <address@hidden>

        * lib/Automake/Channels.pm: New file.
        * lib/Automake/Makefile.am (dist_perllib_DATA): Add Channels.pm.
        * automake.in: Use Automake::Channels and register some channels
        for errors and warnings.
        ($exit_status): Remove, replaced by Channels::$exit_code.
        (%required_variables): Remove, Channels will filter-out duplicates
        itself.
        (initialize_per_input): Call reset_local_duplicates.
        (prog_error): Adjust to all `msg'.
        (setup_warnings): New functions.
        (parse_arguments): Accept -W CATEGORY and --warnings=CATEGORY,
        call setup_warnings.
        (usage): Update usage text accordingly.
        (macro_dump, macros_dump): Return the dump as a string instead
        of printing it.
        (am_install_var) <$warned_about_extra>: Remove, Channels will
        filter-out duplicates itself.
        (set_strictness): Turn on/off channels for each stricness.
        (err, fatal, err_var, err_target, err_am, err_ac, msg_var,
        msg_target, msg_am, msg_ac, reject_var, reject_target, verb):
        New functions, to replace ...
        (print_error, am_error, file_error, macro_error, target_error,
        conf_error, file_warning): ... these functions.  Remove them.
        Update all the code to use the new functions.  The rough
        correspondance is
           am_error                -> err_am
           file_error              -> err
           macro_error             -> err_var
           target_error            -> err_target
           conf_error              -> err_ac
           die                     -> fatal
           macro_error if defined  -> reject_var
           target_error if defined -> reject_target
           verbose                 -> verb
        * automake.texi (Invoking Automake): Document -W and --warnings.
        Remove the documentation for --Werror and --Wno-error.
        * tests/defs: Use -Werror, no --Werror.
        * tests/exeext2.test: Test that the error message
        is enabled with -Wobsolete.
        * tests/output5.test: Rewrite to test that Automake complains
        when there is no Makefile specified. (The original test was
        succeeding for the wrong reason.)
        * tests/seenc.test: Don't use --Wno-error, there is no reason now
        that -Werror doesn't stop after the first error.
        * tests/subobj.test: Use --add-missing, and check that
        `compile' is installed and that Automake says so.
        * tests/subobj2.test: Don't create `compile'.

Index: TODO
===================================================================
RCS file: /cvs/automake/automake/TODO,v
retrieving revision 1.419
diff -u -r1.419 TODO
--- TODO        17 Jun 2002 12:20:16 -0000      1.419
+++ TODO        5 Jul 2002 16:30:38 -0000
@@ -13,9 +13,6 @@
 remove _LTLIBRARIES and just use _LIBRARIES
 then use this for zip/jar as well
 
-consider `sub am_unique_error', which only prints a given error
-message once.  Use eg in lang_c_rewrite for ansi2knr error
-
 for 1.5
 investigate problems with conditionally defined libraries
 
@@ -342,8 +339,7 @@
 * Must rewrite am_install_var.  Should break into multiple functions.
   This will allow the callers to be a little smarter.
 * Rewrite clean targets.
-* Must rewrite error handling code.  Right now it is a real mess
-  Should fix up require_file junk at the same time
+* Fix up require_file junk.
 
 djm wants ``LINKS'' variable; list of things to link together after
 install.  In BSD environment, use:
@@ -414,8 +410,8 @@
 Some things for --strictness=gnits:
 * "cd $(foo); something" is an error in a rule.  Should be:
   "cd $(foo) && something"
-* Look for 'ln -s' and warn about using $(LN) and AC_PROG_LN_S
-* Look for $(LN) and require AC_PROG_LN_S
+* Look for 'ln -s' and warn about using $(LN_S) and AC_PROG_LN_S
+* Look for $(LN_S) and require AC_PROG_LN_S
 
 Auto-distribute "ChangeLog.[0-9]+"?  "ChangeLog.[a-z]+"?
 
Index: automake.in
===================================================================
RCS file: /cvs/automake/automake/automake.in,v
retrieving revision 1.1317
diff -u -r1.1317 automake.in
--- automake.in 3 Jul 2002 18:50:03 -0000       1.1317
+++ automake.in 5 Jul 2002 16:31:01 -0000
@@ -113,6 +113,7 @@
 use strict 'vars', 'subs';
 use Automake::General;
 use Automake::XFile;
+use Automake::Channels;
 use File::Basename;
 use Carp;
 
@@ -306,10 +307,6 @@
 # included in generated Makefile.in.
 my $cmdline_use_dependencies = 1;
 
-# This holds our (eventual) exit status.  We don't actually exit until
-# we have processed all input files.
-my $exit_status = 0;
-
 # From the Perl manual.
 my $symlink_exists = (eval 'symlink ("", "");', $@ eq '');
 
@@ -673,12 +670,6 @@
 # the named of the helper variable used to append to VAR in CONDITIONS.
 my %appendvar = ();
 
-# Variables required via &require_variables.
-# FIXME: This is a temporary hack so that &require_variables prints error
-# messages only once.  It should not be needed the day we have an error
-# reporting function which can print an error message only once.
-my %required_variables = ();
-
 
 ## --------------------------------- ##
 ## Forward subroutine declarations.  ##
@@ -693,6 +684,8 @@
 # (Re)-Initialize per-Makefile.am variables.
 sub initialize_per_input ()
 {
+    reset_local_duplicates ();
+
     $am_file_name = '';
     $am_relative_dir = '';
 
@@ -834,13 +827,47 @@
     %subobjvar = ();
 
     %appendvar = ();
-
-    %required_variables = ();
 }
 
 
 ################################################################
 
+# Initialize our list of error/warning channels.
+# Do not forget to update &usage and the manual
+# if you add or change a warning channel.
+
+# Fatal errors.
+register_channel 'fatal', type => 'fatal';
+# Common errors.
+register_channel 'error', type => 'error';
+# Errors related to GNU Standards.
+register_channel 'error-gnu', type => 'error';
+# Errors related to GNU Standards that should be warnings in `foreign' mode.
+register_channel 'error-gnu/warn', type => 'error';
+# Errors related to GNITS Standards (silent by default).
+register_channel 'error-gnits', type => 'error', silent => 1;
+# Internal errors.
+register_channel 'automake', type => 'fatal',
+  header => ("####################\n" .
+            "## Internal Error ##\n" .
+            "####################\n"),
+  footer => "\nPlease contact <address@hidden>.";
+
+# Warnings about unsupported (or mis-supported) features.
+register_channel 'unsupported', type => 'warning';
+# Unused variables.
+register_channel 'unused', type => 'warning';
+# Warnings about obsolete features (silent by default).
+register_channel 'obsolete', type => 'warning', silent => 1;
+# Warnings about non-portable construct.
+register_channel 'portability', type => 'warning', silent => 1;
+
+# For &verb.
+register_channel 'verb', type => 'debug', silent => 1;
+# Informative messages.
+register_channel 'note', type => 'debug', silent => 0;
+
+
 # Initialize our list of languages that are internally supported.
 
 # C.
@@ -1059,53 +1086,180 @@
 # Do configure.ac scan only once.
 &scan_autoconf_files;
 
-die "$me: no `Makefile.am' found or specified\n"
-    if ! @input_files;
+&fatal ("no `Makefile.am' found or specified\n")
+  if ! @input_files;
 
 my $automake_has_run = 0;
 
 do
 {
-    if ($automake_has_run)
+  if ($automake_has_run)
     {
-       print "$me: processing Makefiles another time to fix them up.\n";
-       &prog_error ("running more than two times should never be needed.")
-           if $automake_has_run >= 2;
+      verb 'processing Makefiles another time to fix them up.';
+      &prog_error ('running more than two times should never be needed.')
+       if $automake_has_run >= 2;
     }
-    $automake_needs_to_reprocess_all_files = 0;
+  $automake_needs_to_reprocess_all_files = 0;
 
-    # Now do all the work on each file.
-    # This guy must be local otherwise it's private to the loop.
-    use vars '$am_file';
-    local $am_file;
-    foreach $am_file (@input_files)
+  # Now do all the work on each file.
+  # This guy must be local otherwise it's private to the loop.
+  use vars '$am_file';
+  local $am_file;
+  foreach $am_file (@input_files)
     {
-       if (! -f ($am_file . '.am'))
+      if (! -f ($am_file . '.am'))
        {
-           &am_error ("`" . $am_file . ".am' does not exist");
+         err "`$am_file.am' does not exist";
        }
-       else
+      else
        {
-           &generate_makefile ($output_files{$am_file}, $am_file);
+         &generate_makefile ($output_files{$am_file}, $am_file);
        }
     }
-    ++$automake_has_run;
+  ++$automake_has_run;
 }
 while ($automake_needs_to_reprocess_all_files);
 
-exit $exit_status;
+exit $exit_code;
 
 ################################################################
 
-# prog_error (@PRINT-ME)
-# ----------------------
-# Signal a programming error, display PRINT-ME, and exit 1.
-sub prog_error (@)
+# Error reporting functions.
+
+# prog_error ($MESSAGE, [%OPTIONS])
+# -------------------------------
+# Signal a programming error, display $MESSAGE, and exit 1.
+sub prog_error ($;%)
+{
+  my ($msg, %opts) = @_;
+  msg 'automake', '', $msg, %opts;
+}
+
+# err ($WHERE, $MESSAGE, [%OPTIONS])
+# err ($MESSAGE)
+# ----------------------------------
+# Uncategorized errors.
+sub err ($;$%)
+{
+  my ($where, $msg, %opts) = @_;
+  msg ('error', $where, $msg, %opts);
+}
+
+# fatal ($WHERE, $MESSAGE, [%OPTIONS])
+# fatal ($MESSAGE)
+# ----------------------------------
+# Fatal errors.
+sub fatal ($;$%)
+{
+  my ($where, $msg, %opts) = @_;
+  msg ('fatal', $where, $msg, %opts);
+}
+
+# err_var ($VARNAME, $MESSAGE, [%OPTIONS])
+# ----------------------------------------
+# Uncategorized errors about variables.
+sub err_var ($$;%)
+{
+  msg_var ('error', @_);
+}
+
+# err_target ($TARGETNAME, $MESSAGE, [%OPTIONS])
+# ----------------------------------------------
+# Uncategorized errors about targets.
+sub err_target ($$;%)
+{
+  msg_target ('error', @_);
+}
+
+# err_am ($MESSAGE, [%OPTIONS])
+# -----------------------------
+# Uncategorized errors about the current Makefile.am.
+sub err_am ($;%)
+{
+  msg_am ('error', @_);
+}
+
+# err_ac ($MESSAGE, [%OPTIONS])
+# -----------------------------
+# Uncategorized errors about configure.ac.
+sub err_ac ($;%)
+{
+  msg_ac ('error', @_);
+}
+
+# msg_var ($CHANNEL, $VARNAME, $MESSAGE, [%OPTIONS])
+# --------------------------------------------------
+# Messages about variables.
+sub msg_var ($$$;%)
+{
+  my ($channel, $macro, $msg, %opts) = @_;
+  msg $channel, $var_location{$macro}, $msg, %opts;
+}
+
+# msg_target ($CHANNEL, $TARGETNAME, $MESSAGE, [%OPTIONS])
+# --------------------------------------------------------
+# Messages about targets.
+sub msg_target ($$$;%)
 {
-    print STDERR "$me: programming error: @_\n";
-    exit 1;
+  my ($channel, $target, $msg, %opts) = @_;
+  msg $channel, $targets{$target}, $msg, %opts;
 }
 
+# msg_am ($CHANNEL, $MESSAGE, [%OPTIONS])
+# ---------------------------------------
+# Messages about about the current Makefile.am.
+sub msg_am ($$;%)
+{
+  my ($channel, $msg, %opts) = @_;
+  msg $channel, "${am_file}.am", $msg, %opts;
+}
+
+# msg_ac ($CHANNEL, $MESSAGE, [%OPTIONS])
+# ---------------------------------------
+# Messages about about configure.ac.
+sub msg_ac ($$;%)
+{
+  my ($channel, $msg, %opts) = @_;
+  msg $channel, $configure_ac, $msg, %opts;
+}
+
+# $BOOL
+# reject_var ($VAR, $ERROR_MSG)
+# ----------------------------------
+sub reject_var ($$)
+{
+  my ($var, $msg) = @_;
+  if (variable_defined ($var))
+    {
+      err_var $var, $msg;
+      return 1;
+    }
+  return 0;
+}
+
+# $BOOL
+# reject_target ($VAR, $ERROR_MSG)
+# --------------------------------
+sub reject_target ($$)
+{
+  my ($target, $msg) = @_;
+  if (target_defined ($target))
+    {
+      err_target $target, $msg;
+      return 1;
+    }
+  return 0;
+}
+
+# verb ($MESSAGE, [%OPTIONS])
+# ---------------------------
+sub verb ($;%)
+{
+  my ($msg, %opts) = @_;
+  msg 'verb', '', $msg, %opts;
+}
+
+################################################################
 
 # subst ($TEXT)
 # -------------
@@ -1183,6 +1337,40 @@
 
 ################################################################
 
+# Handle --warning=CATEGORY or -WCATEGORY
+sub setup_warnings ($$)
+{
+  my ($opt, $cat) = @_;
+  my $has_no = 0;
+
+  if ($cat =~ /^no-(.*)$/)
+    {
+      $cat = $1;
+      $has_no = 1;
+    }
+
+  if ($cat eq 'all')
+    {
+      setup_channel_type 'warning', silent => $has_no;
+    }
+  elsif ($cat eq 'none')
+    {
+      setup_channel_type 'warning', silent => 1 - $has_no;
+    }
+  elsif ($cat eq 'error')
+    {
+      $warnings_are_errors = 1 - $has_no;
+    }
+  elsif (channel_type ($cat) eq 'warning')
+    {
+      setup_channel $cat, silent => $has_no;
+    }
+  else
+    {
+      msg 'unsupported', "unknown warning category `$cat'";
+    }
+}
+
 # Parse command line.
 sub parse_arguments ()
 {
@@ -1207,20 +1395,23 @@
        'o|output-dir:s'        => \$output_directory,
        'a|add-missing'         => \$add_missing,
        'c|copy'        => \$copy_missing,
-       'v|verbose'     => \$verbose,
-       'Werror'         => sub { $SIG{"__WARN__"} = sub { die $_[0] } },
-       'Wno-error'      => sub { $SIG{"__WARN__"} = 'DEFAULT' }
+       'v|verbose'     => sub { setup_channel 'verb', silent => 0; },
+       'W|warnings:s'   => \&setup_warnings,
+       # These long options (--Werror and --Wno-error) for backward
+       # compatibility.  Use -Werror and -Wno-error today.
+       'Werror'         => sub { setup_warnings 'W', 'error'; },
+       'Wno-error'      => sub { setup_warnings 'W', 'no-error'; },
       )
        or exit 1;
 
     if (defined $output_directory)
     {
-       print STDERR "$0: `--output-dir' is deprecated\n";
+      msg 'obsolete', "`--output-dir' is deprecated\n";
     }
     else
     {
-       # In the next release we'll remove this entirely.
-       $output_directory = '.';
+      # In the next release we'll remove this entirely.
+      $output_directory = '.';
     }
 
     foreach my $arg (@ARGV)
@@ -1247,7 +1438,7 @@
          # automake input file is found.  Maybe not the best way, but
          # it is easy to explain.
          $input =~ s/\.in$//
-           or die "$me: invalid input file name `$arg'\n.";
+           or fatal "invalid input file name `$arg'\n.";
        }
       push (@input_files, $input);
       $output_files{$input} = join (':', ($local, @rest));
@@ -1294,12 +1485,10 @@
 
     # There are a few install-related variables that you should not define.
     foreach my $var ('PRE_INSTALL', 'POST_INSTALL', 'NORMAL_INSTALL')
-    {
-       if (variable_defined ($var) && !$var_is_am{$var})
-       {
-           macro_error ($var, "`$var' should not be defined");
-       }
-    }
+      {
+       reject_var $var, "`$var' should not be defined"
+         if ! $var_is_am{$var};
+      }
 
     # At the toplevel directory, we might need config.guess, config.sub
     # or libtool scripts (ltconfig and ltmain.sh).
@@ -1417,10 +1606,10 @@
     if (-e "$out_file")
     {
        unlink ($out_file)
-           or die "$me: cannot remove $out_file: $!\n";
+           or fatal "cannot remove $out_file: $!\n";
     }
     my $gm_file = new Automake::XFile "> $out_file";
-    verbose "creating ", $makefile, ".in";
+    verb "creating $makefile.in";
 
     print $gm_file $output_vars;
     # We make sure that `all:' is the first target.
@@ -1498,24 +1687,24 @@
 # Return 0 if the required version is satisfied, 1 otherwise.
 sub version_check ($)
 {
-    my ($required) = @_;
-    my @version = version_split $VERSION;
-    my @required = version_split $required;
-
-    prog_error ("version is incorrect: $VERSION")
-       if $#version == -1;
-
-    # This should not happen, because process_option_list and split_version
-    # use similar regexes.
-    prog_error ("required version is incorrect: $required")
-       if $#required == -1;
+  my ($required) = @_;
+  my @version = version_split $VERSION;
+  my @required = version_split $required;
+
+  prog_error "version is incorrect: $VERSION"
+    if $#version == -1;
+
+  # This should not happen, because process_option_list and split_version
+  # use similar regexes.
+  prog_error "required version is incorrect: $required"
+    if $#required == -1;
 
-    # If we require 3.4n-foo then we require something
-    # >= 3.4n, with the `foo' fork identifier.
-    return 1
-       if ($required[4] ne '' && $required[4] ne $version[4]);
+  # If we require 3.4n-foo then we require something
+  # >= 3.4n, with the `foo' fork identifier.
+  return 1
+    if ($required[4] ne '' && $required[4] ne $version[4]);
 
-    return 0 > version_compare @version, @required;
+  return 0 > version_compare @version, @required;
 }
 
 # $BOOL
@@ -1526,74 +1715,60 @@
 # handling global options.
 sub process_option_list
 {
-    my ($config, @list) = @_;
-    foreach (@list)
+  my ($config, @list) = @_;
+
+  my $where = ($config ?
+              $seen_init_automake :
+              $var_location{'AUTOMAKE_OPTIONS'});
+
+  foreach (@list)
     {
-       $options{$_} = 1;
-       if ($_ eq 'gnits' || $_ eq 'gnu' || $_ eq 'foreign')
+      $options{$_} = 1;
+      if ($_ eq 'gnits' || $_ eq 'gnu' || $_ eq 'foreign')
        {
-           &set_strictness ($_);
+         &set_strictness ($_);
        }
-       elsif ($_ eq 'cygnus')
+      elsif ($_ eq 'cygnus')
        {
-           $cygnus_mode = 1;
+         $cygnus_mode = 1;
        }
-       elsif (/^(.*\/)?ansi2knr$/)
+      elsif (/^(.*\/)?ansi2knr$/)
        {
-           # An option like "../lib/ansi2knr" is allowed.  With no
-           # path prefix, we assume the required programs are in this
-           # directory.  We save the actual option for later.
-           $options{'ansi2knr'} = $_;
+         # An option like "../lib/ansi2knr" is allowed.  With no
+         # path prefix, we assume the required programs are in this
+         # directory.  We save the actual option for later.
+         $options{'ansi2knr'} = $_;
        }
-       elsif ($_ eq 'no-installman' || $_ eq 'no-installinfo'
-              || $_ eq 'dist-shar' || $_ eq 'dist-zip'
-              || $_ eq 'dist-tarZ' || $_ eq 'dist-bzip2'
-              || $_ eq 'dejagnu' || $_ eq 'no-texinfo.tex'
-              || $_ eq 'readme-alpha' || $_ eq 'check-news'
-              || $_ eq 'subdir-objects' || $_ eq 'nostdinc'
-              || $_ eq 'no-exeext' || $_ eq 'no-define'
-              || $_ eq 'std-options')
+      elsif ($_ eq 'no-installman' || $_ eq 'no-installinfo'
+            || $_ eq 'dist-shar' || $_ eq 'dist-zip'
+            || $_ eq 'dist-tarZ' || $_ eq 'dist-bzip2'
+            || $_ eq 'dejagnu' || $_ eq 'no-texinfo.tex'
+            || $_ eq 'readme-alpha' || $_ eq 'check-news'
+            || $_ eq 'subdir-objects' || $_ eq 'nostdinc'
+            || $_ eq 'no-exeext' || $_ eq 'no-define'
+            || $_ eq 'std-options')
        {
-           # Explicitly recognize these.
+         # Explicitly recognize these.
        }
-       elsif ($_ eq 'no-dependencies')
+      elsif ($_ eq 'no-dependencies')
        {
-           $use_dependencies = 0;
+         $use_dependencies = 0;
        }
-       elsif (/^\d+\.\d+(?:\.\d+)?[a-z]?(?:-[A-Za-z0-9]+)?$/)
+      elsif (/^\d+\.\d+(?:\.\d+)?[a-z]?(?:-[A-Za-z0-9]+)?$/)
        {
-           # Got a version number.
-           if (version_check $&)
+         # Got a version number.
+         if (version_check $&)
            {
-               if ($config)
-               {
-                   file_error ($seen_init_automake,
-                               "require version $_, but have $VERSION");
-                   # Arrange to process this global option only once, otherwise
-                   # the error message would be printed for each Makefile.
-                   $global_options =~ s/(?:^| )$_(?: |$)/ /g;
-               }
-               else
-               {
-                   macro_error ('AUTOMAKE_OPTIONS',
-                                "require version $_, but have $VERSION");
-               }
+             err ($where, "require version $_, but have $VERSION",
+                  uniq_scope => US_GLOBAL);
                return 1;
            }
        }
-       else
+      else
        {
-           if ($config)
-           {
-               file_error ($seen_init_automake,
-                           "option `" . $_ . "\' not recognized");
-           }
-           else
-           {
-               macro_error ('AUTOMAKE_OPTIONS',
-                            "option `" . $_ . "\' not recognized");
-           }
-           return 1;
+         err ($where, "option `$_' not recognized",
+              uniq_scope => US_GLOBAL);
+         return 1;
        }
     }
 }
@@ -1788,8 +1963,9 @@
            my $flags = $lang->flags || '';
            my $val = "${derived}_${flags}";
 
-           prog_error ("found $lang->name in handle_languages, but compiler 
not defined")
-               unless defined $lang->compile;
+           prog_error ("found " . $lang->name .
+                       " in handle_languages, but compiler not defined")
+             unless defined $lang->compile;
 
            (my $obj_compile = $lang->compile) =~ s/\(AM_$flags/\($val/;
            my $obj_ltcompile = '$(LIBTOOL) --mode=compile ' . $obj_compile;
@@ -1904,8 +2080,9 @@
 
        foreach my $file (@files)
        {
-         macro_error ($prefix . $one_file . '_SOURCES',
-                      "automatically discovered file `$file' should not be 
explicitly mentioned")
+         err_var ($prefix . $one_file . '_SOURCES',
+                  "automatically discovered file `$file' should not" .
+                  " be explicitly mentioned")
            if defined $libsources{$file};
        }
     }
@@ -1940,8 +2117,10 @@
         # Configure substitutions in _SOURCES variables are errors.
         if (/address@hidden@$/)
         {
-            macro_error ($var,
-                        "`$var' includes configure substitution `$_', and is 
referred to from `$topparent': configure substitutions not allowed in _SOURCES 
variables");
+            err_var ($var,
+                    "`$var' includes configure substitution `$_', and is " .
+                    "referred to\nfrom `$topparent': configure " .
+                    "substitutions are not allowed\nin _SOURCES variables");
             next;
         }
 
@@ -2041,8 +2220,8 @@
                 require_conf_file ("$am_file.am", FOREIGN, 'compile')
                     if $lang->name eq 'c';
 
-                prog_error ("$lang->name flags defined without compiler")
-                   if ! defined $lang->compile;
+               prog_error ($lang->name . " flags defined without compiler")
+                 if ! defined $lang->compile;
 
                 $renamed = 1;
             }
@@ -2110,13 +2289,9 @@
             next;
         }
 
-        if (defined $object_map{$object})
-        {
-            if ($object_map{$object} ne $full)
-            {
-                am_error ("object `$object' created by `$full' and 
`$object_map{$object}'");
-            }
-        }
+       err_am "object `$object' created by `$full' and `$object_map{$object}'"
+         if (defined $object_map{$object}
+             && $object_map{$object} ne $full);
 
        my $comp_val = (($object =~ /\.lo$/)
                        ? COMPILE_LIBTOOL : COMPILE_ORDINARY);
@@ -2127,9 +2302,9 @@
            && ($object_compilation_map{$comp_obj}
                != (COMPILE_LIBTOOL | COMPILE_ORDINARY))
            && $object_compilation_map{$comp_obj} != $comp_val)
-       {
-           am_error ("object `$object' created both with libtool and without");
-       }
+         {
+           err_am "object `$object' created both with libtool and without";
+         }
        $object_compilation_map{$comp_obj} |= $comp_val;
 
        if (defined $lang)
@@ -2139,18 +2314,18 @@
        }
 
        if ($derived_source)
-       {
-           prog_error ("$lang->name has automatic dependency tracking")
-               if $lang->autodep ne 'no';
+         {
+           prog_error ($lang->name . " has automatic dependency tracking")
+             if $lang->autodep ne 'no';
            # Make sure this new source file is handled next.  That will
            # make it appear to be at the right place in the list.
            unshift (@files, $object);
            # Distribute derived sources unless the source they are
            # derived from is not.
            &push_dist_common ($object)
-               unless ($topparent =~ /^(:?nobase_)?nodist_/);
+             unless ($topparent =~ /^(:?nobase_)?nodist_/);
            next;
-       }
+         }
 
         $linkers_used{$linker} = 1;
 
@@ -2178,9 +2353,9 @@
                # For Java, the way we're handling it right now, a
                # `..' component doesn't make sense.
                 if ($lang->name eq 'java' && $object =~ /(\/|^)\.\.\//)
-                {
-                    am_error ("`$full' contains `..' component but should 
not");
-                }
+                 {
+                   err_am "`$full' should not contain a `..' component";
+                 }
 
                # Make sure object is removed by `make mostlyclean'.
                $compile_clean_files{$object} = MOSTLY_CLEAN;
@@ -2257,7 +2432,7 @@
 
     if (defined $vars_scanned{$var})
     {
-       macro_error ($var, "variable `$var' recursively defined");
+       err_var $var, "variable `$var' recursively defined";
        return "";
     }
     $vars_scanned{$var} = 1;
@@ -2404,13 +2579,9 @@
 
     my ($linker) = '';
 
-    if (variable_defined ($one_file . "_OBJECTS"))
-    {
-       macro_error ($one_file . '_OBJECTS',
-                    $one_file . '_OBJECTS', 'should not be defined');
-       # No point in continuing.
-       return;
-    }
+    # No point in continuing if _OBJECTS is defined.
+    return if reject_var ($one_file . '_OBJECTS',
+                         $one_file . '_OBJECTS should not be defined');
 
     my %used_pfx = ();
     my $needlinker;
@@ -2494,8 +2665,8 @@
 {
     my ($xname, $var) = @_;
 
-    prog_error ("handle_lib_objects: $var undefined")
-       if ! variable_defined ($var);
+    prog_error "handle_lib_objects: $var undefined"
+      if ! variable_defined ($var);
 
     my $ret = 0;
     foreach my $cond (variable_conditions_recursive ($var))
@@ -2531,16 +2702,16 @@
                # Skip -dlopen and -dlpreopen; these are explicitly allowed.
                next if $lsearch =~ /^-dl(pre)?open$/;
                my $prefix = $1 || 'AM_';
-               macro_error ($var,
-                            "linker flags such as `$lsearch' belong in 
`${prefix}LDFLAGS");
+               err_var ($var, "linker flags such as `$lsearch' belong in "
+                        . "`${prefix}LDFLAGS");
            }
            else
            {
                $var =~ /^(.*)LIBADD$/;
                # Only get this error once.
                $flagvar = 1;
-               macro_error ($var,
-                            "linker flags such as `$lsearch' belong in 
`${1}LDFLAGS");
+               err_var ($var, "linker flags such as `$lsearch' belong in "
+                        . "`${1}LDFLAGS");
            }
        }
 
@@ -2565,8 +2736,8 @@
            if (! keys %libsources
                && ! variable_defined ($lt . 'LIBOBJS'))
            {
-               macro_error ($var,
-                            "address@hidden" . "LIBOBJS\@ seen but never set 
in `$configure_ac'");
+               err_var ($var, "address@hidden@ seen but never set in "
+                        . "`$configure_ac'");
            }
 
            foreach my $iter (keys %libsources)
@@ -2604,9 +2775,9 @@
            my $myobjext = ($1 ? 'l' : '') . 'o';
 
            push (@dep_list, $lsearch);
-           macro_error ($var,
-                        "address@hidden" . "ALLOCA\@ seen but `AC_FUNC_ALLOCA' 
not in `$configure_ac'")
-               if ! defined $libsources{'alloca.c'};
+           err_var ($var, "address@hidden@ seen but `AC_FUNC_ALLOCA' not in "
+                    . "`$configure_ac'")
+             if ! defined $libsources{'alloca.c'};
            $dep_files{'$(DEPDIR)/alloca.P' . $myobjext} = 1;
            require_file_with_macro ($var, FOREIGN, 'alloca.c');
            &saw_extension ('c');
@@ -2643,20 +2814,18 @@
 # list of suffixes to check for.
 sub check_canonical_spelling
 {
-    my ($name, @suffixes) = @_;
+  my ($name, @suffixes) = @_;
 
-    my $xname = &canonicalize ($name);
-    if ($xname ne $name)
+  my $xname = &canonicalize ($name);
+  if ($xname ne $name)
     {
-       foreach my $xt (@suffixes)
+      foreach my $xt (@suffixes)
        {
-           macro_error ("$name$xt",
-                        "invalid variable `$name$xt'; should be `$xname$xt'")
-               if variable_defined ("$name$xt");
+         reject_var ("$name$xt", "use `$xname$xt', not `$name$xt'");
        }
     }
 
-    return $xname;
+  return $xname;
 }
 
 
@@ -2696,7 +2865,7 @@
        }
        else
        {
-           prog_error ("invalid entry in \%compile_clean_files");
+         prog_error 'invalid entry in %compile_clean_files';
        }
     }
 
@@ -2813,12 +2982,8 @@
            $xt = '_SOURCES'
        }
 
-       if (variable_defined ($xname . '_LIBADD'))
-       {
-           macro_error ($xname . '_LIBADD',
-                        "use `" . $xname . "_LDADD', not `"
-                        . $xname . "_LIBADD'");
-       }
+       reject_var ($xname . '_LIBADD',
+                   "use `${xname}_LDADD', not `${xname}_LIBADD'");
 
        if (! variable_defined ($xname . '_LDFLAGS'))
        {
@@ -2899,11 +3064,11 @@
     {
        # Check that the library fits the standard naming convention.
        if (basename ($onelib) !~ /^lib.*\.a/)
-       {
+         {
            # FIXME should put line number here.  That means mapping
            # from library name back to variable name.
-           &am_error ("`$onelib' is not a standard library name");
-       }
+           err_am "`$onelib' is not a standard library name";
+         }
 
        my $obj = &get_object_extension ($onelib);
 
@@ -2931,12 +3096,8 @@
            &define_variable ($xlib . "_LIBADD", '');
        }
 
-       if (variable_defined ($xlib . '_LDADD'))
-       {
-           macro_error ($xlib . '_LDADD',
-                        "use `" . $xlib . "_LIBADD', not `"
-                        . $xlib . "_LDADD'");
-       }
+       reject_var ($xlib . '_LDADD',
+                   "use `${xlib}_LIBADD', not `${xlib}_LDADD'");
 
        # Make sure we at look at this.
        &examine_variable ($xlib . '_DEPENDENCIES');
@@ -2992,7 +3153,8 @@
          {
            if ($instdirs{$_})
              {
-               am_error ("`$_' is already going to be installed in 
`$instdirs{$_}'");
+               err_am ("`$_' is already going to be installed in "
+                       . "`$instdirs{$_}'");
              }
            else
              {
@@ -3030,12 +3192,12 @@
                $libname_rx = "\.la";
        }
        if (basename ($onelib) !~ /$libname_rx$/)
-       {
-           # FIXME this should only be a warning for foreign packages
+         {
            # FIXME should put line number here.  That means mapping
            # from library name back to variable name.
-           &am_error ("`$onelib' is not a standard libtool library name");
-       }
+           msg_am ('error-gnu/warn',
+                   "`$onelib' is not a standard libtool library name");
+         }
 
        if (variable_defined ($xlib . '_LIBADD'))
        {
@@ -3051,12 +3213,8 @@
            &define_variable ($xlib . "_LIBADD", '');
        }
 
-       if (variable_defined ($xlib . '_LDADD'))
-       {
-           macro_error ($xlib . '_LDADD',
-                        "use `" . $xlib . "_LIBADD', not `"
-                        . $xlib . "_LDADD'");
-       }
+       reject_var ("${xlib}_LDADD",
+                   "use `${xlib}_LIBADD', not `${xlib}_LDADD'");
 
        # Make sure we at look at this.
        &examine_variable ($xlib . '_DEPENDENCIES');
@@ -3120,21 +3278,18 @@
 # EXTRA_ variables don't contain configure substitutions.
 sub check_typos ()
 {
-    # It is ok if the user sets this particular variable.
-    &examine_variable ('AM_LDFLAGS');
+  # It is ok if the user sets this particular variable.
+  &examine_variable ('AM_LDFLAGS');
 
-    foreach my $varname (keys %var_value)
+  foreach my $varname (keys %var_value)
     {
-       foreach my $primary ('_SOURCES', '_LIBADD', '_LDADD', '_LDFLAGS',
-                            '_DEPENDENCIES')
+      foreach my $primary ('_SOURCES', '_LIBADD', '_LDADD', '_LDFLAGS',
+                          '_DEPENDENCIES')
        {
-           macro_error ($varname,
-                        "invalid unused variable name: `$varname'")
-               # Note that a configure variable is always legitimate.
-               # It is natural to name such variables after the
-               # primary, so we explicitly allow it.
-               if $varname =~ /$primary$/ && ! $content_seen{$varname}
-                  && ! exists $configure_vars{$varname};
+         msg_var 'unused', $varname, "unused variable: `$varname'"
+           # Note that a configure variable is always legitimate.
+           if ($varname =~ /$primary$/ && ! $content_seen{$varname}
+               && ! exists $configure_vars{$varname});
        }
     }
 }
@@ -3188,7 +3343,7 @@
     my @syncodeindexes = ();
 
     my $texi = new Automake::XFile "< $filename";
-    verbose "reading $filename";
+    verb "reading $filename";
 
     my ($outfile, $vfile);
     while ($_ = $texi->getline)
@@ -3198,8 +3353,7 @@
         $outfile = $1;
         if ($outfile =~ /\.(.+)$/ && $1 ne 'info')
           {
-            file_error ("$filename:$.",
-                       "output `$outfile' has unrecognized extension");
+           err "$filename:$.", "output `$outfile' has unrecognized extension";
             return;
           }
       }
@@ -3240,7 +3394,7 @@
 
     if ($outfile eq '')
       {
-       &am_error ("`$filename' missing address@hidden");
+       err_am "`$filename' missing address@hidden";
        return;
       }
 
@@ -3258,18 +3412,10 @@
 # Handle all Texinfo source; helper for handle_texinfo
 sub handle_texinfo_helper
 {
-    macro_error ('TEXINFOS',
-                "`TEXINFOS' is an anachronism; use `info_TEXINFOS'")
-       if variable_defined ('TEXINFOS');
-    return (0, '') if (! variable_defined ('info_TEXINFOS')
-                      && ! variable_defined ('html_TEXINFOS'));
-
-    if (variable_defined ('html_TEXINFOS'))
-    {
-       macro_error ('html_TEXINFOS',
-                    "HTML generation not yet supported");
-       return (0, '');
-    }
+    reject_var 'TEXINFOS', "`TEXINFOS' is an anachronism; use `info_TEXINFOS'";
+    reject_var 'html_TEXINFOS', "HTML generation not yet supported";
+
+    return (0, '') if ! variable_defined ('info_TEXINFOS');
 
     my @texis = &variable_value_as_list_recursive ('info_TEXINFOS', 'all');
 
@@ -3286,11 +3432,11 @@
         $infobase =~ s/\.(txi|texinfo|texi)$//;
 
        if ($infobase eq $info_cursor)
-       {
+         {
            # FIXME: report line number.
-           &am_error ("texinfo file `$info_cursor' has unrecognized 
extension");
+           err_am "texinfo file `$info_cursor' has unrecognized extension";
            next;
-       }
+         }
        $texi_suffixes{$1} = 1;
 
        # If 'version.texi' is referenced by input file, then include
@@ -3302,8 +3448,9 @@
 
        if ($vtexi)
        {
-           &am_error ("`$vtexi', included in `$info_cursor', also included in 
`$versions{$vtexi}'")
-               if (defined $versions{$vtexi});
+           err_am ("`$vtexi', included in `$info_cursor', "
+                   . "also included in `$versions{$vtexi}'")
+             if defined $versions{$vtexi};
            $versions{$vtexi} = $info_cursor;
 
            # We number the stamp-vti files.  This is doable since the
@@ -3459,8 +3606,7 @@
 # Handle any man pages.
 sub handle_man_pages
 {
-    macro_error ('MANS', "`MANS' is an anachronism; use `man_MANS'")
-       if variable_defined ('MANS');
+    reject_var 'MANS', "`MANS' is an anachronism; use `man_MANS'";
 
     # Find all the sections in use.  We do this by first looking for
     # "standard" sections, and then looking for any additional
@@ -3565,10 +3711,10 @@
                                          'DIRS'   => "@tag_deps"));
        &examine_variable ('TAGS_DEPENDENCIES');
     }
-    elsif (variable_defined ('TAGS_DEPENDENCIES'))
+    elsif (reject_var ('TAGS_DEPENDENCIES',
+                      "doesn't make sense to define `TAGS_DEPENDENCIES'"
+                      . "without\nsources or `ETAGS_ARGS'"))
     {
-       macro_error ('TAGS_DEPENDENCIES',
-                    "doesn't make sense to define `TAGS_DEPENDENCIES' without 
sources or `ETAGS_ARGS'");
     }
     else
     {
@@ -3780,13 +3926,13 @@
 
        if (! -d $am_relative_dir . '/' . $dir)
        {
-           macro_error ('SUBDIRS',
-                        "required directory $am_relative_dir/$dir does not 
exist");
+           err_var ('SUBDIRS', "required directory $am_relative_dir/$dir "
+                    . "does not exist");
            next;
        }
 
-       macro_error ('SUBDIRS', "directory should not contain `/'")
-           if $dir =~ /\//;
+       err_var 'SUBDIRS', "directory should not contain `/'"
+         if $dir =~ /\//;
     }
 
     $output_rules .= &file_contents ('subdirs');
@@ -4048,9 +4194,9 @@
     define_variable ('mkinstalldirs',
                     ('$(SHELL) ' . $config_aux_dir . '/mkinstalldirs'));
 
-    macro_error ('CONFIG_HEADER',
-                "`CONFIG_HEADER' is an anachronism; now determined from 
`$configure_ac'")
-       if variable_defined ('CONFIG_HEADER');
+    reject_var ('CONFIG_HEADER',
+               "`CONFIG_HEADER' is an anachronism; now determined "
+               . "automatically\nfrom `$configure_ac'");
 
     my @config_h;
     foreach my $spec (@config_headers)
@@ -4090,7 +4236,7 @@
        }
 
        # Automake files should not be stored in here, but in %MAKE_LIST.
-        prog_error ("$lfile in address@hidden")
+        prog_error "$lfile in address@hidden"
          if -f $file . '.am';
 
        my $local = basename ($file);
@@ -4161,23 +4307,21 @@
 
 sub handle_gettext
 {
-    return if ! $seen_gettext || $relative_dir ne '.';
+  return if ! $seen_gettext || $relative_dir ne '.';
 
-    if (! variable_defined ('SUBDIRS'))
+  if (! variable_defined ('SUBDIRS'))
     {
-       conf_error ("AM_GNU_GETTEXT used but SUBDIRS not defined");
-       return;
+      err_ac "AM_GNU_GETTEXT used but SUBDIRS not defined";
+      return;
     }
 
-    my @subdirs = &variable_value_as_list_recursive ('SUBDIRS', 'all');
-    macro_error ('SUBDIRS',
-                "AM_GNU_GETTEXT used but `po' not in SUBDIRS")
-       if ! grep ('po', @subdirs);
-    macro_error ('SUBDIRS',
-                "AM_GNU_GETTEXT used but `intl' not in SUBDIRS")
-       if ! grep ('intl', @subdirs);
+  my @subdirs = &variable_value_as_list_recursive ('SUBDIRS', 'all');
+  err_var 'SUBDIRS', "AM_GNU_GETTEXT used but `po' not in SUBDIRS"
+    if ! grep ('po', @subdirs);
+  err_var 'SUBDIRS', "AM_GNU_GETTEXT used but `intl' not in SUBDIRS"
+    if ! grep ('intl', @subdirs);
 
-    require_file ($ac_gettext_location, GNU, 'ABOUT-NLS');
+  require_file ($ac_gettext_location, GNU, 'ABOUT-NLS');
 }
 
 # Handle footer elements.
@@ -4188,10 +4332,8 @@
     $output_vars .= 'SOURCES = ' . variable_value ('SOURCES') . "\n\n"
       if variable_value ('SOURCES');
 
-
-    target_error ('.SUFFIXES',
-                 "use variable `SUFFIXES', not target `.SUFFIXES'")
-      if target_defined ('.SUFFIXES');
+    reject_target ('.SUFFIXES',
+                  "use variable `SUFFIXES', not target `.SUFFIXES'");
 
     # Note: AIX 4.1 /bin/make will fail if any suffix rule appears
     # before .SUFFIXES.  So we make sure that .SUFFIXES appears before
@@ -4398,35 +4540,26 @@
 # Handle everything related to gathered targets.
 sub handle_factored_dependencies
 {
-    # Reject bad hooks.
-    foreach my $utarg ('uninstall-data-local', 'uninstall-data-hook',
-                      'uninstall-exec-local', 'uninstall-exec-hook')
-    {
-       if (&target_defined ($utarg))
-       {
-           my $x = $utarg;
-           $x =~ s/(data|exec)-//;
-           target_error ($utarg, "use `$x', not `$utarg'");
-       }
-    }
+  # Reject bad hooks.
+  foreach my $utarg ('uninstall-data-local', 'uninstall-data-hook',
+                    'uninstall-exec-local', 'uninstall-exec-hook')
+    {
+      my $x = $utarg;
+      $x =~ s/(data|exec)-//;
+      reject_target ($utarg, "use `$x', not `$utarg'");
+    }
+
+  reject_target ('install-local',
+                "use `install-data-local' or `install-exec-local', "
+                . "not `install-local'");
+
+  reject_target ('install-info-local',
+                "`install-info-local' target defined but "
+                . "`no-installinfo' option not in use")
+    unless defined $options{'no-installinfo'};
 
-    if (&target_defined ('install-local'))
-    {
-       target_error ('install-local',
-                     "use `install-data-local' or `install-exec-local', "
-                     . "not `install-local'");
-    }
-
-    if (!defined $options{'no-installinfo'}
-       && &target_defined ('install-info-local'))
-    {
-       target_error ('install-info-local',
-                     "`install-info-local' target defined but "
-                     . "`no-installinfo' option not in use");
-    }
-
-    # Install the -local hooks.
-    foreach (keys %dependencies)
+  # Install the -local hooks.
+  foreach (keys %dependencies)
     {
       # Hooks are installed on the -am targets.
       s/-am$// or next;
@@ -4437,9 +4570,9 @@
        }
     }
 
-    # Install the -hook hooks.
-    # FIXME: Why not be as liberal as we are with -local hooks?
-    foreach ('install-exec', 'install-data', 'uninstall')
+  # Install the -hook hooks.
+  # FIXME: Why not be as liberal as we are with -local hooks?
+  foreach ('install-exec', 'install-data', 'uninstall')
     {
       if (&target_defined ("$_-hook"))
        {
@@ -4449,22 +4582,22 @@
        }
     }
 
-    # All the required targets are phony.
-    depend ('.PHONY', keys %required_targets);
+  # All the required targets are phony.
+  depend ('.PHONY', keys %required_targets);
 
-    # Actually output gathered targets.
-    foreach (sort target_cmp keys %dependencies)
+  # Actually output gathered targets.
+  foreach (sort target_cmp keys %dependencies)
     {
-        # If there is nothing about this guy, skip it.
-        next
-         unless (@{$dependencies{$_}}
-                 || $actions{$_}
-                 || $required_targets{$_});
-        &pretty_print_rule ("$_:", "\t",
-                           uniq (sort @{$dependencies{$_}}));
-       $output_rules .= $actions{$_}
-         if defined $actions{$_};
-        $output_rules .= "\n";
+      # If there is nothing about this guy, skip it.
+      next
+       unless (@{$dependencies{$_}}
+               || $actions{$_}
+               || $required_targets{$_});
+      &pretty_print_rule ("$_:", "\t",
+                         uniq (sort @{$dependencies{$_}}));
+      $output_rules .= $actions{$_}
+      if defined $actions{$_};
+      $output_rules .= "\n";
     }
 }
 
@@ -4481,24 +4614,23 @@
 # Handle TESTS variable and other checks.
 sub handle_tests
 {
-    if (defined $options{'dejagnu'})
+  if (defined $options{'dejagnu'})
     {
-        &handle_tests_dejagnu;
+      &handle_tests_dejagnu;
     }
-    else
+  else
     {
-       foreach my $c ('DEJATOOL', 'RUNTEST', 'RUNTESTFLAGS')
+      foreach my $c ('DEJATOOL', 'RUNTEST', 'RUNTESTFLAGS')
        {
-           macro_error ($c,
-                        "`$c' defined but `dejagnu' not in `AUTOMAKE_OPTIONS'")
-             if variable_defined ($c);
+         reject_var ($c, "`$c' defined but `dejagnu' not in "
+                     . "`AUTOMAKE_OPTIONS'");
        }
     }
 
-    if (variable_defined ('TESTS'))
+  if (variable_defined ('TESTS'))
     {
-       push (@check_tests, 'check-TESTS');
-       $output_rules .= &file_contents ('check');
+      push (@check_tests, 'check-TESTS');
+      $output_rules .= &file_contents ('check');
     }
 }
 
@@ -4551,8 +4683,7 @@
        next
          if $curs eq 'EXTRA';
 
-       macro_error ($curs . '_JAVA',
-                    "multiple _JAVA primaries in use")
+       err_var "${curs}_JAVA", "multiple _JAVA primaries in use"
          if defined $dir;
        $dir = $curs;
       }
@@ -4565,22 +4696,22 @@
 # Handle some of the minor options.
 sub handle_minor_options
 {
-    if (defined $options{'readme-alpha'})
+  if (defined $options{'readme-alpha'})
     {
-       if ($relative_dir eq '.')
+      if ($relative_dir eq '.')
        {
-           if ($package_version !~ /^$GNITS_VERSION_PATTERN$/)
+         if ($package_version !~ /^$GNITS_VERSION_PATTERN$/)
+           {
+             msg ('error-gnits', $package_version_location,
+                  "version `$package_version' doesn't follow " .
+                  "Gnits standards");
+           }
+         if (defined $1 && -f 'README-alpha')
            {
-               # FIXME: allow real filename.
-               file_error ($package_version_location,
-                           "version `$package_version' doesn't follow Gnits 
standards");
-           }
-           elsif (defined $1 && -f 'README-alpha')
-           {
-               # This means we have an alpha release.  See
-               # GNITS_VERSION_PATTERN for details.
-               require_file_with_macro ('AUTOMAKE_OPTIONS',
-                                        FOREIGN, 'README-alpha');
+             # This means we have an alpha release.  See
+             # GNITS_VERSION_PATTERN for details.
+             require_file_with_macro ('AUTOMAKE_OPTIONS',
+                                      FOREIGN, 'README-alpha');
            }
        }
     }
@@ -4678,7 +4809,7 @@
                   map { "--trace=$_" . ':\$f:\$l::\$n::\${::}%' } @traced);
 
   my $tracefh = new Automake::XFile ("$traces $filename |");
-  verbose "reading $traces";
+  verb "reading $traces";
 
   while ($_ = $tracefh->getline)
     {
@@ -4737,12 +4868,12 @@
        }
       elsif ($macro eq 'AM_AUTOMAKE_VERSION')
         {
-         file_error ($here,
-                     "version mismatch.  This is Automake $VERSION,\n" .
-                     "but the definition used by this AM_INIT_AUTOMAKE\n" .
-                     "comes from Automake $args[1].  You should recreate\n" .
-                     "aclocal.m4 with aclocal and run automake again.\n")
-             if ($VERSION ne $args[1]);
+         err ($here,
+              "version mismatch.  This is Automake $VERSION,\n" .
+              "but the definition used by this AM_INIT_AUTOMAKE\n" .
+              "comes from Automake $args[1].  You should recreate\n" .
+              "aclocal.m4 with aclocal and run automake again.\n")
+           if ($VERSION ne $args[1]);
 
          $seen_automake_version = 1;
         }
@@ -4793,7 +4924,7 @@
     %libsources = ();
 
     $configure_ac = find_configure_ac;
-    die "$me: `configure.ac' or `configure.in' is required\n"
+    fatal "`configure.ac' or `configure.in' is required\n"
         if !$configure_ac;
 
     scan_autoconf_traces ($configure_ac);
@@ -4807,27 +4938,27 @@
 
     @configure_input_files = sort keys %make_list;
 
-    conf_error ("`AM_INIT_AUTOMAKE' must be used")
-       if ! $seen_init_automake;
+    err_ac "`AM_INIT_AUTOMAKE' must be used"
+      if ! $seen_init_automake;
 
     if (! $seen_automake_version)
-    {
+      {
        if (-f 'aclocal.m4')
-       {
-           file_error ($seen_init_automake || $me,
-                       "your implementation of AM_INIT_AUTOMAKE comes from " .
-                       "an\nold Automake version.  You should recreate " .
-                       "aclocal.m4\nwith aclocal and run automake again.\n");
-       }
+         {
+           err ($seen_init_automake || $me,
+                "your implementation of AM_INIT_AUTOMAKE comes from " .
+                "an\nold Automake version.  You should recreate " .
+                "aclocal.m4\nwith aclocal and run automake again.\n");
+         }
        else
-       {
-           file_error ($seen_init_automake || $me,
-                       "no proper implementation of AM_INIT_AUTOMAKE was " .
-                       "found,\nprobably because aclocal.m4 is missing...\n" .
-                       "You should run aclocal to create this file, then\n" .
-                       "run automake again.\n");
-       }
-    }
+         {
+           err ($seen_init_automake || $me,
+                "no proper implementation of AM_INIT_AUTOMAKE was " .
+                "found,\nprobably because aclocal.m4 is missing...\n" .
+                "You should run aclocal to create this file, then\n" .
+                "run automake again.\n");
+         }
+      }
 
     # Look for some files we need.  Always check for these.  This
     # check must be done for every run, even those where we are only
@@ -4838,8 +4969,8 @@
     $relative_dir = '.';
     require_conf_file ($configure_ac, FOREIGN,
                       'install-sh', 'mkinstalldirs', 'missing');
-    am_error ("`install.sh' is an anachronism; use `install-sh' instead")
-        if -f $config_aux_path[0] . '/install.sh';
+    err_am "`install.sh' is an anachronism; use `install-sh' instead"
+      if -f $config_aux_path[0] . '/install.sh';
 
     # Preserve dist_common for later.
     $configure_dist_common = variable_value ('DIST_COMMON', 'TRUE') || '';
@@ -4850,56 +4981,49 @@
 # Set up for Cygnus mode.
 sub check_cygnus
 {
-    return unless $cygnus_mode;
+  return unless $cygnus_mode;
 
-    &set_strictness ('foreign');
-    $options{'no-installinfo'} = 1;
-    $options{'no-dependencies'} = 1;
-    $use_dependencies = 0;
+  &set_strictness ('foreign');
+  $options{'no-installinfo'} = 1;
+  $options{'no-dependencies'} = 1;
+  $use_dependencies = 0;
 
-    conf_error ("`AM_MAINTAINER_MODE' required when --cygnus specified")
-      if !$seen_maint_mode;
+  err_ac "`AM_MAINTAINER_MODE' required when --cygnus specified"
+    if !$seen_maint_mode;
 }
 
 # Do any extra checking for GNU standards.
 sub check_gnu_standards
 {
-    if ($relative_dir eq '.')
+  if ($relative_dir eq '.')
     {
-       # In top level (or only) directory.
+      # In top level (or only) directory.
 
-       # Accept one of these three licenses; default to COPYING.
-       my $license = 'COPYING';
-        foreach (qw /COPYING.LIB COPYING.LESSER/)
+      # Accept one of these three licenses; default to COPYING.
+      my $license = 'COPYING';
+      foreach (qw /COPYING.LIB COPYING.LESSER/)
        {
-           $license = $_ if -f $_;
+         $license = $_ if -f $_;
        }
-       require_file ("$am_file.am", GNU, $license,
-                     qw/INSTALL NEWS README AUTHORS ChangeLog/);
-    }
-
-    if ($strictness >= GNU
-       && defined $options{'no-installman'})
-    {
-       macro_error ('AUTOMAKE_OPTIONS',
-                    "option `no-installman' disallowed by GNU standards");
+      require_file ("$am_file.am", GNU, $license,
+                   qw/INSTALL NEWS README AUTHORS ChangeLog/);
     }
 
-    if ($strictness >= GNU
-       && defined $options{'no-installinfo'})
+  for my $opt ('no-installman', 'no-installinfo')
     {
-       macro_error ('AUTOMAKE_OPTIONS',
-                    "option `no-installinfo' disallowed by GNU standards");
+      msg_var ('error-gnu', 'AUTOMAKE_OPTIONS',
+              "option `$opt' disallowed by GNU standards")
+       if (defined $options{$opt});
     }
 }
 
 # Do any extra checking for GNITS standards.
 sub check_gnits_standards
 {
-    if ($relative_dir eq '.')
+  if ($relative_dir eq '.')
     {
-       # In top level (or only) directory.
-       require_file ("$am_file.am", GNITS, 'THANKS');
+      # In top level (or only) directory.
+      require_file ("$am_file.am", GNITS, 'THANKS');
     }
 }
 
@@ -4928,40 +5052,37 @@
 # Rewrite a single C source file.
 sub lang_c_rewrite
 {
-    my ($directory, $base, $ext) = @_;
+  my ($directory, $base, $ext) = @_;
 
-    if (defined $options{'ansi2knr'} && $base =~ /_$/)
+  if (defined $options{'ansi2knr'} && $base =~ /_$/)
     {
-       # FIXME: include line number in error.
-       am_error ("C source file `$base.c' would be deleted by ansi2knr rules");
+      # FIXME: include line number in error.
+      err_am "C source file `$base.c' would be deleted by ansi2knr rules";
     }
 
-    my $r = LANG_PROCESS;
-    if (defined $options{'subdir-objects'})
+  my $r = LANG_PROCESS;
+  if (defined $options{'subdir-objects'})
     {
-       $r = LANG_SUBDIR;
-       $base = $directory . '/' . $base
-           unless $directory eq '.' || $directory eq '';
+      $r = LANG_SUBDIR;
+      $base = $directory . '/' . $base
+       unless $directory eq '.' || $directory eq '';
 
-       if (! $seen_cc_c_o)
-       {
-           # Only give error once.
-           $seen_cc_c_o = 1;
-           # FIXME: line number.
-           am_error ("C objects in subdir but `AM_PROG_CC_C_O' not in 
`$configure_ac'");
-       }
+      err_am ("C objects in subdir but `AM_PROG_CC_C_O' "
+             . "not in `$configure_ac'",
+             uniq_scope => US_GLOBAL)
+       unless $seen_cc_c_o;
 
-       require_conf_file ("$am_file.am", FOREIGN, 'compile');
+      require_conf_file ("$am_file.am", FOREIGN, 'compile');
 
-       # In this case we already have the directory information, so
-       # don't add it again.
-       $de_ansi_files{$base} = '';
+      # In this case we already have the directory information, so
+      # don't add it again.
+      $de_ansi_files{$base} = '';
     }
-    else
+  else
     {
-       $de_ansi_files{$base} = (($directory eq '.' || $directory eq '')
-                                ? ''
-                                : "$directory/");
+      $de_ansi_files{$base} = (($directory eq '.' || $directory eq '')
+                              ? ''
+                              : "$directory/");
     }
 
     return $r;
@@ -5185,29 +5306,23 @@
 
 sub lang_yacc_finish
 {
-    return if defined $language_scratch{'yacc-done'};
-    $language_scratch{'yacc-done'} = 1;
+  return if defined $language_scratch{'yacc-done'};
+  $language_scratch{'yacc-done'} = 1;
 
-    macro_error ('YACCFLAGS',
-                "`YACCFLAGS' obsolete; use `YFLAGS' instead")
-      if variable_defined ('YACCFLAGS');
+  reject_var 'YACCFLAGS', "`YACCFLAGS' obsolete; use `YFLAGS' instead";
 
-    if (count_files_for_language ('yacc') > 1)
-    {
-       &yacc_lex_finish_helper;
-    }
+  &yacc_lex_finish_helper
+    if count_files_for_language ('yacc') > 1;
 }
 
 
 sub lang_lex_finish
 {
-    return if defined $language_scratch{'lex-done'};
-    $language_scratch{'lex-done'} = 1;
+  return if defined $language_scratch{'lex-done'};
+  $language_scratch{'lex-done'} = 1;
 
-    if (count_files_for_language ('lex') > 1)
-    {
-       &yacc_lex_finish_helper;
-    }
+  &yacc_lex_finish_helper
+    if count_files_for_language ('lex') > 1;
 }
 
 
@@ -5578,7 +5693,7 @@
 {
   my ($negate, $cond, $where) = @_;
 
-  file_error ($where, "$cond does not appear in AM_CONDITIONAL")
+  err $where, "$cond does not appear in AM_CONDITIONAL"
     if ! $configure_cond{$cond} && $cond !~ /^TRUE|FALSE$/;
 
   $cond = "${cond}_TRUE"
@@ -5601,7 +5716,7 @@
 
   if (! @cond_stack)
     {
-      file_error ($where, "else without if");
+      err $where, "else without if";
       return;
     }
 
@@ -5615,9 +5730,8 @@
       $cond = condition_negate ($cond)
        if $negate;
 
-      file_error ($where,
-                 "else reminder ($negate$cond) incompatible with "
-                 . "current conditional: $cond_stack[$#cond_stack]")
+      err ($where, "else reminder ($negate$cond) incompatible with "
+          . "current conditional: $cond_stack[$#cond_stack]")
        if $cond_stack[$#cond_stack] ne $cond;
     }
 
@@ -5635,7 +5749,7 @@
 
   if (! @cond_stack)
     {
-      file_error ($where, "endif without if: $negate$cond");
+      err $where, "endif without if: $negate$cond";
       return;
     }
 
@@ -5648,9 +5762,8 @@
       $cond = condition_negate ($cond)
        if $negate;
 
-      file_error ($where,
-                 "endif reminder ($negate$cond) incompatible with "
-                 . "current conditional: $cond_stack[$#cond_stack]")
+      err ($where, "endif reminder ($negate$cond) incompatible with "
+          . "current conditional: $cond_stack[$#cond_stack]")
        if $cond_stack[$#cond_stack] ne $cond;
     }
 
@@ -5676,13 +5789,10 @@
 # ambiguity.
 sub check_ambiguous_conditional ($$)
 {
-    my ($var, $cond) = @_;
-    my $message = conditional_ambiguous_p ($var, $cond);
-    if ($message ne '')
-    {
-       macro_error ($var, $message);
-       macro_dump ($var);
-    }
+  my ($var, $cond) = @_;
+  my $message = conditional_ambiguous_p ($var, $cond);
+  err_var $var, "$message\n" . macro_dump ($var)
+    if $message;
 }
 
 # $STRING
@@ -5773,38 +5883,38 @@
 #
 sub variable_not_always_defined_in_cond ($$)
 {
-    my ($var, $cond) = @_;
+  my ($var, $cond) = @_;
 
-    # It's easy to answer if the variable is not defined.
-    return ("TRUE",) unless exists $var_value{$var};
+  # It's easy to answer if the variable is not defined.
+  return ("TRUE",) unless exists $var_value{$var};
 
-    # How does it work?  Let's take the second example:
-    #
-    #   variable_not_always_defined_in_cond ('A', 'COND1_TRUE')
-    #
-    # (1) First, we get the list of conditions where A is defined:
-    #
-    #   ("COND1_TRUE COND2_TRUE", "COND1_TRUE COND2_FALSE", "COND3_TRUE")
-    #
-    # (2) Then we generate the set of inverted conditions:
-    #
-    #   ("COND1_FALSE COND2_TRUE COND3_FALSE",
-    #    "COND1_FALSE COND2_FALSE COND3_FALSE")
-    #
-    # (3) Finally we remove these conditions which are not implied by
-    #     COND1_TRUE.  This yields an empty list and we are done.
-
-    my @res = ();
-    my @cond_defs = keys %{$var_value{$var}}; # (1)
-    foreach my $icond (invert_conditions (@cond_defs)) # (2)
+  # How does it work?  Let's take the second example:
+  #
+  #   variable_not_always_defined_in_cond ('A', 'COND1_TRUE')
+  #
+  # (1) First, we get the list of conditions where A is defined:
+  #
+  #   ("COND1_TRUE COND2_TRUE", "COND1_TRUE COND2_FALSE", "COND3_TRUE")
+  #
+  # (2) Then we generate the set of inverted conditions:
+  #
+  #   ("COND1_FALSE COND2_TRUE COND3_FALSE",
+  #    "COND1_FALSE COND2_FALSE COND3_FALSE")
+  #
+  # (3) Finally we remove these conditions which are not implied by
+  #     COND1_TRUE.  This yields an empty list and we are done.
+
+  my @res = ();
+  my @cond_defs = keys %{$var_value{$var}}; # (1)
+  foreach my $icond (invert_conditions (@cond_defs)) # (2)
     {
-       prog_error ("invert_conditions returned an input condition")
-           if exists $var_value{$var}{$icond};
+      prog_error "invert_conditions returned an input condition"
+       if exists $var_value{$var}{$icond};
 
-       push @res, $icond
-           if (conditional_true_when ($cond, $icond)); # (3)
+      push @res, $icond
+       if (conditional_true_when ($cond, $icond)); # (3)
     }
-    return @res;
+  return @res;
 }
 
 # &macro_define($VAR, $VAR_IS_AM, $TYPE, $COND, $VALUE, $WHERE)
@@ -5814,7 +5924,7 @@
 {
   my ($var, $var_is_am, $type, $cond, $value, $where) = @_;
 
-  file_error ($where, "bad macro name `$var'")
+  err $where, "bad characters in macro name `$var'"
     if $var !~ /$MACRO_PATTERN/o;
 
   $cond ||= 'TRUE';
@@ -5824,19 +5934,14 @@
   # `:=', and later promoted to `+='.
   if ($var_is_am)
     {
-      if (defined $var_type{$var} && $var_type{$var} ne $type)
-       {
-         file_error ($where,
-                     ("$var was set with `$var_type{$var}=' "
-                      . "and is now set with `$type='"));
-       }
+      err ($where, "$var was set with `$var_type{$var}=' "
+          . "and is now set with `$type='")
+       if defined $var_type{$var} && $var_type{$var} ne $type;
     }
   else
     {
-      if (!defined $var_type{$var} && $type eq '+')
-       {
-         file_error ($where, "$var must be set with `=' before using `+='");
-       }
+      err $where, "$var must be set with `=' before using `+='"
+       if !defined $var_type{$var} && $type eq '+';
     }
   $var_type{$var} = $type;
 
@@ -5926,13 +6031,13 @@
          my @undef_cond = variable_not_always_defined_in_cond $var, $cond;
          if (@undef_cond != 0)
            {
-             file_error ($where,
-                         "Cannot apply `+=' because `$var' is not defined "
-                         . "in\nthe following conditions:\n  "
-                         . join ("\n  ", @undef_cond)
-                         . "\nEither define `$var' in these conditions,"
-                         . " or use\n`+=' in the same conditions as"
-                         . " the definitions.");
+             err ($where,
+                  "Cannot apply `+=' because `$var' is not defined "
+                  . "in\nthe following conditions:\n  "
+                  . join ("\n  ", @undef_cond)
+                  . "\nEither define `$var' in these conditions,"
+                  . " or use\n`+=' in the same conditions as"
+                  . " the definitions.");
            }
          else
            {
@@ -5955,12 +6060,9 @@
       # just don't let it do.
       if (defined $var_value{$var}{$cond} && !$var_is_am{$var} && $var_is_am)
        {
-         if ($verbose)
-           {
-             print STDERR "$me: refusing to override the user definition 
of:\n";
-             macro_dump ($var);
-             print STDERR "$me: with `$cond' => `$value'\n";
-           }
+         verb ("refusing to override the user definition of:\n"
+               . macro_dump ($var)
+               ."with `$cond' => `$value'");
        }
       else
        {
@@ -6022,26 +6124,27 @@
 sub macro_dump ($)
 {
   my ($var) = @_;
+  my $text = '';
 
   if (!exists $var_value{$var})
     {
-      print STDERR "  $var does not exist\n";
+      $text = "  $var does not exist\n";
     }
   else
     {
       my $var_is_am = $var_is_am{$var} ? "Automake" : "User";
       my $where = (defined $var_location{$var}
                   ? $var_location{$var} : "undefined");
-      print STDERR "$var_comment{$var}"
+      $text .= "$var_comment{$var}"
        if defined $var_comment{$var};
-      print STDERR "  $var ($var_is_am, where = $where) $var_type{$var}=\n";
-      print STDERR "  {\n";
+      $text .= "  $var ($var_is_am, where = $where) $var_type{$var}=\n  {\n";
       foreach my $vcond (sort by_condition keys %{$var_value{$var}})
        {
-         print STDERR "    $vcond => $var_value{$var}{$vcond}\n";
+         $text .= "    $vcond => $var_value{$var}{$vcond}\n";
        }
-      print STDERR "  }\n";
+      $text .= "  }\n";
     }
+  return $text;
 }
 
 
@@ -6051,13 +6154,13 @@
 {
   my ($var) = @_;
 
-  print STDERR "%var_value =\n";
-  print STDERR "{\n";
+  my $text = "%var_value =\n{\n";
   foreach my $var (sort (keys %var_value))
     {
-      macro_dump ($var);
+      $text .= macro_dump ($var);
     }
-  print STDERR "}\n";
+  $text .= "}\n";
+  return $text;
 }
 
 
@@ -6077,7 +6180,7 @@
     # don't want.
     if (!exists $var_value{$var})
       {
-       macro_error ($var, "`$var' is a target; expected a variable")
+       err_target $var, "`$var' is a target; expected a variable"
          if defined $targets{$var};
        # The variable is not defined
        return 0;
@@ -6106,17 +6209,16 @@
   return 1
     if variable_defined $var;
 
-  macro_error ($where, "variable `$var' not defined");
+  require_variables ($where, "variable `$var' is used", $var);
 
   return 0;
 }
 
-
 # Mark a variable as examined.
 sub examine_variable
 {
-    my ($var) = @_;
-    variable_defined ($var);
+  my ($var) = @_;
+  variable_defined ($var);
 }
 
 
@@ -6217,7 +6319,7 @@
 
     if (defined $vars_scanned{$var})
     {
-       macro_error ($parent, "variable `$var' recursively defined");
+        err_var $parent, "variable `$var' recursively defined";
        return ();
     }
     $vars_scanned{$var} = 1;
@@ -6419,21 +6521,23 @@
 # are using the value of a variable.
 sub check_variable_defined_unconditionally ($$)
 {
-    my ($var, $parent) = @_;
-    foreach my $cond (keys %{$var_value{$var}})
+  my ($var, $parent) = @_;
+  foreach my $cond (keys %{$var_value{$var}})
     {
-        next
-         if $cond =~ /^TRUE|FALSE$/;
+      next
+       if $cond =~ /^TRUE|FALSE$/;
 
-       if ($parent)
+      if ($parent)
        {
-           macro_error ($parent,
-                        "warning: automake does not support conditional 
definition of $var in $parent");
+         msg_var ('unsupported', $parent,
+                  "automake does not support conditional definition of "
+                  . "$var in $parent");
        }
-       else
+      else
        {
-           macro_error ($parent,
-                        "warning: automake does not support $var being defined 
conditionally");
+         msg_var ('unsupported', $var,
+                  "automake does not support $var being defined "
+                  . "conditionally");
        }
     }
 }
@@ -6593,7 +6697,7 @@
     {
        # `vars_scanned' is a global we use to keep track of which
        # variables we've already examined.
-       macro_error ($parent, "variable `$var' recursively defined");
+       err_var $parent, "variable `$var' recursively defined";
     }
     elsif ($cond eq 'all')
     {
@@ -6794,7 +6898,7 @@
 {
   my ($where, $src, $dest) = @_;
 
-  verbose "Sources ending in $src become $dest";
+  verb "Sources ending in $src become $dest";
   push @suffixes, $src, $dest;
 
   # When tranforming sources to objects, Automake uses the
@@ -6877,49 +6981,47 @@
   # though, so we emit a warning.
   (my $noexe = $target) =~ s,\$\(EXEEXT\)$,,;
   if ($noexe ne $target && defined $targets{$noexe})
-  {
+    {
       # The no-exeext option enables this feature.
       if (! defined $options{'no-exeext'})
-      {
-         macro_error ($noexe,
-                      "deprecated feature: `$noexe' overrides 
`$noexe\$(EXEEXT)'\nchange your target to read `$noexe\$(EXEEXT)'");
-      }
+       {
+         msg ('obsolete', $noexe,
+              "deprecated feature: `$noexe' overrides `$noexe\$(EXEEXT)'\n"
+              . "change your target to read `$noexe\$(EXEEXT)'");
+       }
       # Don't define.
       return 0;
-  }
+    }
 
-  if (defined $targets{$target}
-      && ($cond
-         ? ! defined $target_conditional{$target}
-         : defined $target_conditional{$target}))
-  {
-      target_error ($target,
-                   "$target defined both conditionally and unconditionally");
-  }
+  reject_target ($target,
+                "$target defined both conditionally and unconditionally")
+    if ($cond
+       ? ! exists $target_conditional{$target}
+       : exists $target_conditional{$target});
 
   # Value here doesn't matter; for targets we only note existence.
   $targets{$target} = $where;
   if ($cond)
-  {
+    {
       if ($target_conditional{$target})
-      {
+       {
          &check_ambiguous_conditional ($target, $cond);
-      }
+       }
       $target_conditional{$target}{$cond} = $where;
-  }
+    }
 
   # Check the rule for being a suffix rule. If so, store in a hash.
   # Either it's a rule for two known extensions...
   if ($target =~ /^($KNOWN_EXTENSIONS_PATTERN)($KNOWN_EXTENSIONS_PATTERN)$/
-  # ...or it's a rule with unknown extensions (.i.e, the rule looks like
-  # `.foo.bar:' but `.foo' or `.bar' are not declared in SUFFIXES
-  # and are not known language extensions).
-  # Automake will complete SUFFIXES from @suffixes automatically
-  # (see handle_footer).
+      # ...or it's a rule with unknown extensions (.i.e, the rule looks like
+      # `.foo.bar:' but `.foo' or `.bar' are not declared in SUFFIXES
+      # and are not known language extensions).
+      # Automake will complete SUFFIXES from @suffixes automatically
+      # (see handle_footer).
       || ($target =~ /$SUFFIX_RULE_PATTERN/o && accept_extensions($1)))
-  {
-    register_suffix_rule ($where, $1, $2);
-  }
+    {
+      register_suffix_rule ($where, $1, $2);
+    }
 
   return 1;
 }
@@ -6959,7 +7061,7 @@
     my ($amfile) = @_;
 
     my $am_file = new Automake::XFile ("< $amfile");
-    verbose "reading $amfile";
+    verb "reading $amfile";
 
     my $spacing = '';
     my $comment = '';
@@ -6979,9 +7081,8 @@
        }
        elsif (/$WHITE_PATTERN/o)
        {
-           file_error ("$amfile:$.",
-                       "blank line following trailing backslash")
-               if $saw_bk;
+           err "$amfile:$.", "blank line following trailing backslash"
+             if $saw_bk;
            # Stick a single white line before the incoming macro or rule.
            $spacing = "\n";
            $blank = 1;
@@ -7042,16 +7143,16 @@
        {
            # Stick a single white line before the incoming macro or rule.
            $spacing = "\n";
-           file_error ($here, "blank line following trailing backslash")
-               if $saw_bk;
+           err $here, "blank line following trailing backslash"
+             if $saw_bk;
        }
        elsif (/$COMMENT_PATTERN/o)
        {
            # Stick comments before the incoming macro or rule.
            $comment .= $spacing . $_;
            $spacing = '';
-           file_error ($here, "comment following trailing backslash")
-               if $saw_bk && $comment eq '';
+           err $here, "comment following trailing backslash"
+             if $saw_bk && $comment eq '';
            $prev_state = IN_COMMENT;
        }
        elsif ($saw_bk)
@@ -7165,8 +7266,8 @@
            $output_trailer .= &make_condition  (@cond_stack);
            $output_trailer .= $_;
            $comment = $spacing = '';
-           file_error ($here, "`#' comment at start of rule is unportable")
-               if $_ =~ /^\t\s*\#/;
+           err $here, "`#' comment at start of rule is unportable"
+             if $_ =~ /^\t\s*\#/;
        }
 
        $saw_bk = $new_saw_bk;
@@ -7175,18 +7276,9 @@
 
     $output_trailer .= $comment;
 
-    if ("@saved_cond_stack" ne "@cond_stack")
-    {
-       if (@cond_stack)
-       {
-           &am_error ("unterminated conditionals: @cond_stack");
-       }
-       else
-       {
-           # FIXME: better error message here.
-           &am_error ("conditionals not nested in include file");
-       }
-    }
+    err_am (@cond_stack ? "unterminated conditionals: @cond_stack"
+           : "too many conditionals closed in include file")
+      if "@saved_cond_stack" ne "@cond_stack";
 }
 
 
@@ -7218,11 +7310,8 @@
     my ($amfile) = @_;
 
     # This supports the strange variable tricks we are about to play.
-    if (scalar keys %var_value > 0)
-      {
-       macros_dump ();
-       prog_error ("variable defined before read_main_am_file");
-      }
+    prog_error (macros_dump () . "variable defined before read_main_am_file")
+      if (scalar keys %var_value > 0);
 
     # Generate copyright header for generated Makefile.in.
     # We do discard the output of predefined variables, handled below.
@@ -7322,7 +7411,7 @@
     # Swallow the file and apply the COMMAND.
     my $fc_file = new Automake::XFile "< $file";
     # Looks stupid?
-    verbose "reading $file";
+    verb "reading $file";
     my $saved_dollar_slash = $/;
     undef $/;
     $_ = $fc_file->getline;
@@ -7397,9 +7486,9 @@
     foreach (make_paragraphs ($file, %transform))
     {
         # Sanity checks.
-       file_error ($file, "blank line following trailing backslash:\n$_")
+       err $file, "blank line following trailing backslash:\n$_"
          if /\\$/;
-       file_error ($file, "comment following trailing backslash:\n$_")
+       err $file, "comment following trailing backslash:\n$_"
          if /\\#/;
 
        if (/^$/)
@@ -7550,7 +7639,7 @@
        elsif (/$ASSIGNMENT_PATTERN/mso)
        {
            my ($var, $type, $val) = ($1, $2, $3);
-           file_error ($file, "macro `$var' with trailing backslash")
+           err $file, "macro `$var' with trailing backslash"
              if /\\$/;
 
            $is_rule = 0;
@@ -7583,18 +7672,10 @@
        }
     }
 
-    if ("@saved_cond_stack" ne "@cond_stack")
-    {
-       if (@cond_stack)
-       {
-           &am_error ("unterminated conditionals: @cond_stack");
-       }
-       else
-       {
-           # FIXME: better error message here.
-           &am_error ("conditionals not nested in include file");
-       }
-    }
+    err_am (@cond_stack ?
+           "unterminated conditionals: @cond_stack" :
+           "too many conditionals closed in include file")
+      if "@saved_cond_stack" ne "@cond_stack";
 
     return ($comment, $result_vars, $result_rules);
 }
@@ -7734,15 +7815,15 @@
          my ($base, $dist, $X) = ($1 || '', $2 || '', $3 || '');
          if ($dist ne '' && ! $can_dist)
             {
-             macro_error ($varname,
-                          "invalid variable `$varname': `dist' is forbidden");
+             err_var ($varname,
+                      "invalid variable `$varname': `dist' is forbidden");
            }
          # Standard directories must be explicitely allowed.
          elsif (! defined $valid{$X} && exists $standard_prefix{$X})
            {
-             macro_error ($varname,
-                          "`${X}dir' is not a legitimate " .
-                          "directory for `$primary'");
+             err_var ($varname,
+                      "`${X}dir' is not a legitimate directory " .
+                      "for `$primary'");
            }
          # A not explicitely valid directory is allowed if Xdir is defined.
          elsif (! defined $valid{$X} &&
@@ -7812,10 +7893,8 @@
     # allow `JAVA', as it is customarily used to mean the Java
     # interpreter.  This is but one of several Java hacks.  Similarly,
     # `PYTHON' is customarily used to mean the Python interpreter.
-    macro_error ($primary, "`$primary' is an anachronism")
-       if variable_defined ($primary)
-           && ($primary ne 'JAVA' && $primary ne 'PYTHON');
-
+    reject_var $primary, "`$primary' is an anachronism"
+      unless $primary eq 'JAVA' || $primary eq 'PYTHON';
 
     # Get the prefixes which are valid and actually used.
     @prefix = am_primary_prefixes ($primary, $can_dist, @prefix);
@@ -7823,7 +7902,6 @@
     # If a primary includes a configure substitution, then the EXTRA_
     # form is required.  Otherwise we can't properly do our job.
     my $require_extra;
-    my $warned_about_extra = 0;
 
     my @used = ();
     my @result = ();
@@ -7862,12 +7940,9 @@
              {
                if ($nodir_name eq 'EXTRA')
                  {
-                   if (! $warned_about_extra)
-                     {
-                       $warned_about_extra = 1;
-                       macro_error ($one_name,
-                                    "`$one_name' contains configure 
substitution, but shouldn't");
-                     }
+                   err_var ($one_name,
+                            "`$one_name' contains configure substitution, "
+                            . "but shouldn't");
                  }
                # Check here to make sure variables defined in
                # configure.ac do not imply that EXTRA_PRIMARY
@@ -7883,12 +7958,10 @@
 
            push (@result, $rcurs);
          }
-
        # A blatant hack: we rewrite each _PROGRAMS primary to include
        # EXEEXT.
        append_exeext ($one_name)
          if $primary eq 'PROGRAMS';
-
        # "EXTRA" shouldn't be used when generating clean targets,
        # all, or install targets.  We used to warn if EXTRA_FOO was
        # defined uselessly, but this was annoying.
@@ -7941,11 +8014,10 @@
        $output_vars .= "\n";
     }
 
-    if ($require_extra && ! variable_defined ('EXTRA_' . $primary))
-    {
-       macro_error ($require_extra,
-                    "`$require_extra' contains configure substitution, but 
`EXTRA_$primary' not defined");
-    }
+    err_var ($require_extra,
+            "`$require_extra' contains configure substitution,\n"
+            . "but `EXTRA_$primary' not defined")
+      if ($require_extra && ! variable_defined ('EXTRA_' . $primary));
 
     # Push here because PRIMARY might be configure time determined.
     push (@all, '$(' . $primary . ')')
@@ -8153,14 +8225,7 @@
                next
                    if $found_it && $force_missing;
 
-               if ($suppress)
-               {
-                 file_warning ($where, "$message$trailer");
-               }
-               else
-               {
-                 file_error ($where, "$message$trailer");
-               }
+               msg ($suppress ? 'note' : 'error', $where, "$message$trailer");
            }
        }
     }
@@ -8260,31 +8325,43 @@
 # Push a list of files onto dist_common.
 sub push_dist_common
 {
-    prog_error ("push_dist_common run after handle_dist")
-        if $handle_dist_run;
-    macro_define ('DIST_COMMON', 1, '+', '', "@_", '');
+  prog_error "push_dist_common run after handle_dist"
+    if $handle_dist_run;
+  macro_define ('DIST_COMMON', 1, '+', '', "@_", '');
 }
 
 
 # Set strictness.
 sub set_strictness
 {
-    $strictness_name = $_[0];
-    if ($strictness_name eq 'gnu')
-    {
-       $strictness = GNU;
-    }
-    elsif ($strictness_name eq 'gnits')
+  $strictness_name = $_[0];
+  if ($strictness_name eq 'gnu')
     {
-       $strictness = GNITS;
+      $strictness = GNU;
+      setup_channel 'error-gnu', silent => 0;
+      setup_channel 'error-gnu/warn', silent => 0, type => 'error';
+      setup_channel 'error-gnits', silent => 1;
+      setup_channel 'portability', silent => 0;
+    }
+  elsif ($strictness_name eq 'gnits')
+    {
+      $strictness = GNITS;
+      setup_channel 'error-gnu', silent => 0;
+      setup_channel 'error-gnu/warn', silent => 0, type => 'error';
+      setup_channel 'error-gnits', silent => 0;
+      setup_channel 'portability', silent => 0;
+    }
+  elsif ($strictness_name eq 'foreign')
+    {
+      $strictness = FOREIGN;
+      setup_channel 'error-gnu', silent => 1;
+      setup_channel 'error-gnu/warn', silent => 0, type => 'warning';
+      setup_channel 'error-gnits', silent => 1;
+      setup_channel 'portability', silent => 1;
     }
-    elsif ($strictness_name eq 'foreign')
-    {
-       $strictness = FOREIGN;
-    }
-    else
+  else
     {
-       die "$me: level `$strictness_name' not recognized\n";
+      prog_error "level `$strictness_name' not recognized\n";
     }
 }
 
@@ -8301,81 +8378,6 @@
 
 ################################################################
 
-# print_error ($LEADER, @ARGS)
-# ----------------------------
-# Do the work of printing the error message.  Join @ARGS with spaces,
-# then split at newlines and add $LEADER to each line.  Uses `warn' to
-# print message.  Set exit status.
-sub print_error
-{
-    my ($leader, @args) = @_;
-    my $text = "@args";
-    @args = split ("\n", $text);
-    $text = $leader . join ("\n" . $leader, @args) . "\n";
-    warn $text;
-    $exit_status = 1;
-}
-
-
-# Print an error message and set exit status.
-sub am_error (@)
-{
-    print_error ("$me: ${am_file}.am: ", @_);
-}
-
-
-# &file_error ($FILE, @ARGS)
-# --------------------------
-sub file_error ($@)
-{
-    my ($file, @args) = @_;
-    print_error ("$file: ", @args);
-}
-
-
-# &macro_error ($MACRO, @ARGS)
-# ----------------------------
-# Report an error, @ARGS, about $MACRO.
-sub macro_error ($@)
-{
-    my ($macro, @args) = @_;
-    file_error ($var_location{$macro}, @args);
-}
-
-
-# &target_error ($TARGET, @ARGS)
-# ------------------------------
-# Report an error, @ARGS, about the rule $TARGET.
-sub target_error ($@)
-{
-    my ($target, @args) = @_;
-    file_error ($targets{$target}, @args);
-}
-
-
-# Like am_error, but while scanning configure.ac.
-sub conf_error
-{
-    # FIXME: can run in subdirs.
-    print_error ("$me: $configure_ac: ", @_);
-}
-
-# &file_warning ($FILE, @ARGS)
-# ----------------------------
-# Warning message with line number referring to configure.ac.
-# Does not affect exit_status
-sub file_warning ($@)
-{
-    my ($file, @args) = @_;
-
-    my $saved_exit_status = $exit_status;
-    my $sig = $SIG{'__WARN__'};
-    $SIG{'__WARN__'} = 'DEFAULT';
-    file_error ($file, @args);
-    $exit_status = $saved_exit_status;
-    $SIG{'__WARN__'} = $sig;
-}
-
 # INTEGER
 # require_variables ($WHERE, $REASON, @VARIABLES)
 # -----------------------------------------------
@@ -8396,10 +8398,6 @@
 
       ++$res;
 
-      # Don't print the error message twice.
-      next if exists $required_variables{$var};
-      $required_variables{$var} = $where; # The value doesn't matter.
-
       my $text = "$reason`$var' is undefined.";
       if (exists $am_macro_for_var{$var})
        {
@@ -8414,7 +8412,7 @@
            . "`autoconf' again.";
        }
 
-      file_error ($where, $text);
+      err $where, $text, uniq_scope => US_GLOBAL;
     }
   return $res;
 }
@@ -8432,16 +8430,16 @@
 # Print usage information.
 sub usage ()
 {
-    print <<EOF;
-Usage: $0 [OPTION] ... [Makefile]...
+    print "Usage: $0 [OPTION] ... [Makefile]...
 
 Generate Makefile.in for configure from Makefile.am.
 
 Operation modes:
-      --help             print this help, then exit
-      --version          print version number, then exit
-  -v, --verbose          verbosely list files processed
-      --no-force         only update Makefile.in's that are out of date
+      --help               print this help, then exit
+      --version            print version number, then exit
+  -v, --verbose            verbosely list files processed
+      --no-force           only update Makefile.in's that are out of date
+  -W, --warnings=CATEGORY  report the warnings falling in CATEGORY
 
 Dependency tracking:
   -i, --ignore-deps      disable dependency tracking code
@@ -8458,8 +8456,17 @@
       --libdir=DIR       directory storing library files
   -c, --copy             with -a, copy missing files (default is symlink)
   -f, --force-missing    force update of standard files
-EOF
-#' <- unfool perl-mode
+
+Warning categories include:
+  `obsolete'      obsolete features or constructions
+  `unsupported'   unsupported or incomplete features (default)
+  `unused'        unused variables (default)
+  `portability'   portability issues (default in gnu and gnits mode)
+  `all'           all the warnings
+  `no-CATEGORY'   turn off warnings in CATEGORY
+  `none'          turn off all the warnings
+  `error'         treat warnings as errors
+";
 
     my ($last, @lcomm);
     $last = '';
Index: automake.texi
===================================================================
RCS file: /cvs/automake/automake/automake.texi,v
retrieving revision 1.282
diff -u -r1.282 automake.texi
--- automake.texi       26 Jun 2002 19:13:50 -0000      1.282
+++ automake.texi       5 Jul 2002 16:31:21 -0000
@@ -1030,14 +1030,35 @@
 @opindex --version
 Print the version number of Automake and exit.
 
address@hidden --Werror
address@hidden --Wno-error
address@hidden --Werror
address@hidden --Wno-error
address@hidden will cause all warnings issued by @code{automake} to
-become errors.  Errors affect the exit status of @code{automake}, while
-warnings do not.  @samp{--Wno-error}, the default, causes warnings to be
-treated as warnings only.
address@hidden -W CATEGORY
address@hidden address@hidden
address@hidden -W
address@hidden --warnings
+Output warnings falling in @var{category}.  @var{category} can be
+one of:
address@hidden @samp
address@hidden obsolete
+obsolete features or constructions
address@hidden unsupported
+unsupported or incomplete features
address@hidden unused
+unused variables
address@hidden portability
+portability issues
address@hidden all
+all the warnings
address@hidden none
+turn off all the warnings
address@hidden error
+treat warnings as errors
address@hidden table
+
+A category can be turned off by prefixing its name with @samp{no-}.  For
+instance @samp{-Wno-unused} will hide the warnings about unused
+variables.
+
+The categories output by default are @samp{unsupported} and
address@hidden
 @end table
 
 
Index: stamp-vti
===================================================================
RCS file: /cvs/automake/automake/stamp-vti,v
retrieving revision 1.182
diff -u -r1.182 stamp-vti
--- stamp-vti   27 Jun 2002 08:46:44 -0000      1.182
+++ stamp-vti   5 Jul 2002 16:31:21 -0000
@@ -1,4 +1,4 @@
address@hidden UPDATED 27 June 2002
address@hidden UPDATED-MONTH June 2002
address@hidden UPDATED 5 July 2002
address@hidden UPDATED-MONTH July 2002
 @set EDITION 1.6a
 @set VERSION 1.6a
Index: version.texi
===================================================================
RCS file: /cvs/automake/automake/version.texi,v
retrieving revision 1.255
diff -u -r1.255 version.texi
--- version.texi        27 Jun 2002 08:46:44 -0000      1.255
+++ version.texi        5 Jul 2002 16:31:21 -0000
@@ -1,4 +1,4 @@
address@hidden UPDATED 27 June 2002
address@hidden UPDATED-MONTH June 2002
address@hidden UPDATED 5 July 2002
address@hidden UPDATED-MONTH July 2002
 @set EDITION 1.6a
 @set VERSION 1.6a
Index: lib/Automake/Channels.pm
===================================================================
RCS file: lib/Automake/Channels.pm
diff -N lib/Automake/Channels.pm
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ lib/Automake/Channels.pm    5 Jul 2002 16:31:24 -0000
@@ -0,0 +1,519 @@
+# Copyright 2002 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::Channels;
+
+=head1 NAME
+
+Automake::Channels - support functions for error and warning management
+
+=head1 SYNOPSIS
+
+  use Automake::Channels;
+
+  # Register a channel to output warnings about unused variables.
+  register_channel 'unused', type => 'warning';
+
+  # Register a channel for system errors.
+  register_channel 'system', type => 'error', exit_code => 4;
+
+  # Output a message on channel 'unused'.
+  msg 'unused', "$file:$line", "unused variable `$var'";
+
+  # Make the 'unused' channel silent.
+  setup_channel 'unused', silent => 1;
+
+  # Turn on all channels of type 'warning'.
+  setup_channel_type 'warning', silent => 0;
+
+  # Treat all warnings as errors.
+  $warnings_are_errors = 1;
+
+  # Exit with the greater exist code encountered so far.
+  exit $exit_code;
+
+=head1 DESCRIPTION
+
+This perl module provides support functions for handling diagnostic
+channels in programs.  Channels can be registered to convey fatal,
+error, warning, or debug messages.  Each channel has various options
+(e.g. is the channel silent, should duplicate messages be removed,
+etc.) that can also be overridden on a per-message basis.
+
+=cut
+
+use 5.005;
+use strict;
+use Exporter;
+use Carp;
+use File::Basename;
+
+our @ISA = qw (Exporter);
+our @EXPORT = qw ($exit_code $warnings_are_errors
+                 &reset_local_duplicates &reset_global_duplicates
+                 &register_channel &msg &exists_channel &channel_type
+                 &setup_channel &setup_channel_type
+                 US_GLOBAL US_LOCAL
+                 UP_NONE UP_TEXT UP_LOC_TEXT);
+
+our %channels;
+our $me = basename $0;
+
+=head2 Global Variables
+
+=over 4
+
+=item C<$exit_code>
+
+The greatest exit code seen so far. C<$exit_code> is updated from
+the C<exit_code> options of C<fatal> and C<error> channels.
+
+=cut
+
+our $exit_code = 0;
+
+=item C<$warnings_are_errors>
+
+Set this variable to 1 if warning messages should be treated as
+errors (i.e. if they should update C<$exit_code>).
+
+=cut
+
+our $warnings_are_errors = 0;
+
+=back
+
+=head2 Constants
+
+=over 4
+
+=item C<UP_NONE>, C<UP_TEXT>, C<UP_LOC_TEXT>
+
+Possible values for the C<uniq_part> options.  This select the part
+of the message that should be considered when filtering out duplicates.
+If C<UP_LOC_TEXT> is used, the location and the explanation message
+are used for filtering.  If C<UP_TEXT> is used, only the explanation
+message is used (so the same message will be filtered out if it appears
+at different locations).  C<UP_NONE> means that duplicate messages
+should be output.
+
+=cut
+
+use constant UP_NONE => 0;
+use constant UP_TEXT => 1;
+use constant UP_LOC_TEXT => 2;
+
+=item C<US_LOCAL>, C<US_GLOBAL>
+
+Possible values for the C<uniq_scope> options.
+Use C<US_GLOBAL> for error messages that should be printed only
+once in the run of the program, C<US_LOCAL> for message that
+should be printed only once per file.  (Actually, C<Channels> does not
+now when files are changed, it relies on you calling C<reset_local_duplicates>
+when this happens.)
+
+=cut
+
+# possible values for uniq_scope
+use constant US_LOCAL => 0;
+use constant US_GLOBAL => 1;
+
+=back
+
+=head2 Options
+
+Channels accept the options described below.  These options can be
+passed as a hash to the C<register_channel>, C<setup_channel>, and C<msg>
+functions.  The possible keys, with there default value are:
+
+=over
+
+=item C<type =E<gt> 'warning'>
+
+The type of the channel.  One of C<'debug'>, C<'warning'>, C<'error'>, or
+C<'fatal'>.  Fatal messages abort the program when they are output.
+Error messages update the exit status.  Debug and warning messages are
+harmless, except that warnings can be treated as errors of
+C<$warnings_are_errors> is set.
+
+=item C<exit_code =E<gt> 1>
+
+The value to update C<$exit_code> with when a fatal or error message
+is emited.  C<$exit_code> is also updated for warnings output
+when @<$warnings_are_errors> is set.
+
+=item C<file =E<gt> \*STDERR>
+
+The file where the error should be output.
+
+=item C<silent =E<gt> 0>
+
+Whether the channel should be silent.  Use this do disable a
+category of warning, for instance.
+
+=item C<uniq_part =E<gt> UP_LOC_TEXT>
+
+The part of the message subject to duplicate filtering.  See the
+documentation for the C<UP_NONE>, C<UP_TEXT>, and C<UP_LOC_TEXT>
+constants above.
+
+=item C<uniq_scope =E<gt> US_LOCAL>
+
+The scope of duplicate filtering.  See the documentation for the
+C<US_LOCAL>, and C<US_GLOBAL> constants above.
+
+=item C<header =E<gt> ''>
+
+A string to prepend to each message emitted through this channel.
+
+=item C<footer =E<gt> ''>
+
+A string to append to each message emitted through this channel.
+
+=back
+
+=cut
+
+# Default options for a channel.
+our %_default_options =
+  (
+   type => 'warning',
+   exit_code => 1,
+   file => \*STDERR,
+   silent => 0,
+   uniq_scope => US_LOCAL,
+   uniq_part => UP_LOC_TEXT,
+   header => '',
+   footer => '',
+   );
+
+# Filled with output messages as keys, to detect duplicates.
+# The value associated to each keys is the number of occurences
+# filtered out.
+our %_local_duplicate_messages = ();
+our %_global_duplicate_messages = ();
+
+sub _reset_duplicates (\%)
+{
+  my ($ref) = @_;
+  my $dup = 0;
+  foreach my $k (keys %$ref)
+    {
+      $dup += $ref->{$k};
+    }
+  %$ref = ();
+  return $dup;
+}
+
+
+=head2 Functions
+
+=over 4
+
+=item C<reset_local_duplicates ()>
+
+Reset local duplicate messages (see C<US_LOCAL>), and
+return the number of messages that have been filtered out.
+
+=cut
+
+sub reset_local_duplicates ()
+{
+  return _reset_duplicates %_local_duplicate_messages;
+}
+
+=item C<reset_global_duplicates ()>
+
+Reset local duplicate messages (see C<US_GLOBAL>), and
+return the number of messages that have been filtered out.
+
+=cut
+
+sub reset_global_duplicates ()
+{
+  return _reset_duplicates %_global_duplicate_messages;
+}
+
+sub _merge_options (\%%)
+{
+  my ($hash, %options) = @_;
+  local $_;
+
+  foreach (keys %options)
+    {
+      if (exists $hash->{$_})
+       {
+         $hash->{$_} = $options{$_}
+       }
+      else
+       {
+         confess "unknown option `$_'";
+       }
+    }
+}
+
+=item C<register_channel ($name, [%options])>
+
+Declare channel C<$name>, and override the default options
+with those listed in C<%options>.
+
+=cut
+
+sub register_channel ($;%)
+{
+  my ($name, %options) = @_;
+  my %channel_opts = %_default_options;
+  _merge_options %channel_opts, %options;
+  $channels{$name} = \%channel_opts;
+}
+
+=item C<exists_channel ($name)>
+
+Returns true iff channel C<$name> has been registered.
+
+=cut
+
+sub exists_channel ($)
+{
+  my ($name) = @_;
+  return exists $channels{$name};
+}
+
+=item C<channel_type ($name)>
+
+Returns the type of channel C<$name> if it has been registered.
+Returns The empty string otherwise.
+
+=cut
+
+sub channel_type ($)
+{
+  my ($name) = @_;
+  return $channels{$name}{'type'} if exists_channel $name;
+  return '';
+}
+
+# _format_message ($LEADER, $MESSAGE)
+# -----------------------------------
+# Split $MESSAGE at new lines and add $LEADER to each line.
+sub _format_message ($$)
+{
+  my ($leader, $message) = @_;
+  return $leader . join ("\n" . $leader, split ("\n", $message)) . "\n";
+}
+
+# _print_message ($LOCATION, $MESSAGE, %OPTIONS)
+# ----------------------------------------------
+# Format the message, check duplicates, and print it.
+sub _print_message ($$%)
+{
+  my ($location, $message, %opts) = @_;
+
+  return 0 if ($opts{'silent'});
+
+  if ($location)
+    {
+      $location .= ': ';
+    }
+  else
+    {
+      $location = "$me: ";
+    }
+
+  my $msg = _format_message ($location,
+                            $opts{'header'} . $message . $opts{'footer'});
+
+  # Check for duplicate message if requested.
+  if ($opts{'uniq_part'} != UP_NONE)
+    {
+      # Which part of the error should we match?
+      my $to_filter;
+      if ($opts{'uniq_part'} == UP_TEXT)
+       {
+         $to_filter = $message;
+       }
+      elsif ($opts{'uniq_part'} == UP_LOC_TEXT)
+       {
+         $to_filter = $msg;
+       }
+      else
+       {
+         confess "unknown value for uniq_part: " . $opts{'uniq_part'};
+       }
+
+      # Do we want local or global uniqueness?
+      my $dups;
+      if ($opts{'uniq_scope'} == US_LOCAL)
+       {
+         $dups = \%_local_duplicate_messages;
+       }
+      elsif ($opts{'uniq_scope'} == US_GLOBAL)
+       {
+         $dups = \%_global_duplicate_messages;
+       }
+      else
+       {
+         confess "unknown value for uniq_scope: " . $opts{'uniq_scope'};
+       }
+
+      # Update the hash of messages.
+      if (exists $dups->{$to_filter})
+       {
+         ++$dups->{$to_filter};
+         return 0;
+       }
+      else
+       {
+         $dups->{$to_filter} = 0;
+       }
+    }
+  my $file = $opts{'file'};
+  print $file $msg;
+  return 1;
+}
+
+=item C<msg ($channel, $location, $message, [%options])>
+
+Emit a message on C<$channel>, overriding some options of the channel  with
+those specified in C<%options>.  Obviously C<$channel> must have been
+registered with C<register_channel>.
+
+C<$message> is the text of the message, and C<$location> is a location
+associated to the message.
+
+For instance to complain about some unused variable C<mumble>
+declared at line 10 in F<foo.c>, one could do:
+
+  msg 'unused', 'foo.c:10', "unused variable `mumble'";
+
+If channel C<unused> is not silent (and if this message is not a duplicate),
+the following would be output:
+
+  foo.c:10: unused variable `mumble'
+
+If C<$message> contains new line caracters, C<$location> is prepended
+to each line.  For instance
+
+  msg 'error', 'somewhere', "1st line\n2nd line";
+
+becomes
+
+  somewhere: 1st line
+  somewhere: 2nd line
+
+If C<$location> is an empty string, it is replaced by the name of the
+program.  Actually, if you don't use C<%options>, you can even
+elide the empty C<$location>.  Thus
+
+  msg 'fatal', '', 'fatal error';
+  msg 'fatal', 'fatal error';
+
+both print
+
+  progname: fatal error
+
+=cut
+
+sub msg ($$;$%)
+{
+  my ($channel, $location, $message, %options) = @_;
+
+  if (! defined $message)
+    {
+      $message = $location;
+      $location = '';
+    }
+
+  confess "unknown channel $channel" unless exists $channels{$channel};
+
+  my %opts = %{$channels{$channel}};
+  _merge_options (%opts, %options);
+
+  # Print the message if needed.
+  if (_print_message ($location, $message, %opts))
+    {
+      # Adjust exit status.
+      if ($opts{'type'} eq 'error'
+         || $opts{'type'} eq 'fatal'
+         || ($opts{'type'} eq 'warning' && $warnings_are_errors))
+       {
+         my $es = $opts{'exit_code'};
+         $exit_code = $es if $es > $exit_code;
+       }
+
+      # Die on fatal messages.
+      exit $exit_code if $opts{'type'} eq 'fatal';
+    }
+}
+
+
+=item C<setup_channel ($channel, %options)>
+
+Override the options of C<$channel> with those specified by C<%options>.
+
+=cut
+
+sub setup_channel ($%)
+{
+  my ($name, %opts) = @_;
+  confess "channel $name doesn't exist" unless exists $channels{$name};
+  _merge_options %{$channels{$name}}, %opts;
+}
+
+=item C<setup_channel_type ($type, %options)>
+
+Override the options of any channel of type C<$type>
+with those specified by C<%options>.
+
+=cut
+
+sub setup_channel_type ($%)
+{
+  my ($type, %opts) = @_;
+  foreach my $channel (keys %channels)
+    {
+      setup_channel $channel, %opts
+       if $channels{$channel}{'type'} eq $type;
+    }
+}
+
+=back
+
+=head1 HISTORY
+
+Written by Alexandre Duret-Lutz E<lt>F<address@hidden>E<gt>.
+
+=cut
+
+1;
+
+### 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:
Index: lib/Automake/Makefile.am
===================================================================
RCS file: /cvs/automake/automake/lib/Automake/Makefile.am,v
retrieving revision 1.3
diff -u -r1.3 Makefile.am
--- lib/Automake/Makefile.am    15 Jan 2002 23:54:12 -0000      1.3
+++ lib/Automake/Makefile.am    5 Jul 2002 16:31:24 -0000
@@ -1,4 +1,4 @@
 ## Process this file with automake to create Makefile.in
 
 perllibdir = $(pkgvdatadir)/Automake
-dist_perllib_DATA = Struct.pm General.pm XFile.pm
+dist_perllib_DATA = Channels.pm General.pm Struct.pm XFile.pm
Index: lib/Automake/Makefile.in
===================================================================
RCS file: /cvs/automake/automake/lib/Automake/Makefile.in,v
retrieving revision 1.33
diff -u -r1.33 Makefile.in
--- lib/Automake/Makefile.in    26 Jun 2002 19:13:51 -0000      1.33
+++ lib/Automake/Makefile.in    5 Jul 2002 16:31:24 -0000
@@ -88,7 +88,7 @@
 target_alias = @target_alias@
 
 perllibdir = $(pkgvdatadir)/Automake
-dist_perllib_DATA = Struct.pm General.pm XFile.pm
+dist_perllib_DATA = Channels.pm General.pm Struct.pm XFile.pm
 subdir = lib/Automake
 mkinstalldirs = $(SHELL) $(top_srcdir)/lib/mkinstalldirs
 CONFIG_CLEAN_FILES =
Index: tests/defs
===================================================================
RCS file: /cvs/automake/automake/tests/defs,v
retrieving revision 1.54
diff -u -r1.54 defs
--- tests/defs  18 Jun 2002 19:55:58 -0000      1.54
+++ tests/defs  5 Jul 2002 16:31:25 -0000
@@ -121,7 +121,7 @@
 if test -z "$AUTOMAKE"; then
    perllibdir=$srcdir/../lib
    export perllibdir
-   AUTOMAKE="$PERL `pwd`/../../automake --libdir=$srcdir/../lib --foreign 
--Werror"
+   AUTOMAKE="$PERL `pwd`/../../automake --libdir=$srcdir/../lib --foreign 
-Werror"
 fi
 
 # See how aclocal should be run.
Index: tests/exeext2.test
===================================================================
RCS file: /cvs/automake/automake/tests/exeext2.test,v
retrieving revision 1.1
diff -u -r1.1 exeext2.test
--- tests/exeext2.test  27 Jul 2001 00:02:43 -0000      1.1
+++ tests/exeext2.test  5 Jul 2002 16:31:25 -0000
@@ -4,13 +4,15 @@
 
 . $srcdir/defs || exit 1
 
+set -e
+
 cat >> configure.in << 'END'
 AC_PROG_CC
 AC_EXEEXT
 END
 
 cat > Makefile.am << 'END'
-bin_PROGRAMS = maude 
+bin_PROGRAMS = maude
 
 maude:
        yeah
@@ -18,8 +20,14 @@
 
 $ACLOCAL
 
-$AUTOMAKE --Werror && exit 1
+$AUTOMAKE -Wnone
+$AUTOMAKE -Wnone -Wobsolete 2>stderr && exit 1
+cat stderr
+grep maude stderr
+$AUTOMAKE -Wall 2>stderr && exit 1
+cat stderr
+grep maude stderr
 
 echo 'AUTOMAKE_OPTIONS = no-exeext' >> Makefile.am
 
-$AUTOMAKE --Werror
+$AUTOMAKE -Wall
Index: tests/output5.test
===================================================================
RCS file: /cvs/automake/automake/tests/output5.test,v
retrieving revision 1.5
diff -u -r1.5 output5.test
--- tests/output5.test  28 Oct 2001 14:00:28 -0000      1.5
+++ tests/output5.test  5 Jul 2002 16:31:25 -0000
@@ -1,16 +1,20 @@
 #! /bin/sh
 
-# Test for AC_OUTPUT().  From Mark Galassi.
+# Make sure Automake complains when there is no Makefile specified.
 
 . $srcdir/defs || exit 1
 
+set -e
+
 cat > configure.in << 'END'
-AC_OUTPUT()
+AC_INIT([foo], [bar], [baz])
+AM_INIT_AUTOMAKE
+AC_OUTPUT
 END
 
 : > Makefile.am
 
-$ACLOCAL || exit 1
+$ACLOCAL
 $AUTOMAKE > output 2>&1 && exit 1
-grep 'found or specified' output && exit 1
-exit 0
+cat output
+grep 'found or specified' output
Index: tests/seenc.test
===================================================================
RCS file: /cvs/automake/automake/tests/seenc.test,v
retrieving revision 1.5
diff -u -r1.5 seenc.test
--- tests/seenc.test    30 May 2002 06:05:05 -0000      1.5
+++ tests/seenc.test    5 Jul 2002 16:31:25 -0000
@@ -4,6 +4,8 @@
 
 . $srcdir/defs || exit 1
 
+set -e
+
 cat >> configure.in << 'END'
 dnl AC_PROG_CC and AC_PROG_CXX missing on purpose
 AC_LIBOBJ([fsusage])
@@ -19,10 +21,8 @@
 : > fsusage.c
 : > mountlist.c
 
-$ACLOCAL || exit 1
-# Use -Wno-error, because there are many error messages
-# output for this test, and we just want to make sure some of
-# them are about CC and CXX being undefined.
-$AUTOMAKE --Wno-error 2> err && exit 1
-$FGREP CC err || exit 1
-$FGREP CXX err || exit 1
+$ACLOCAL
+$AUTOMAKE 2>err && exit 1
+cat err
+$FGREP CC err
+$FGREP CXX err
Index: tests/subobj.test
===================================================================
RCS file: /cvs/automake/automake/tests/subobj.test,v
retrieving revision 1.6
diff -u -r1.6 subobj.test
--- tests/subobj.test   30 May 2002 06:05:05 -0000      1.6
+++ tests/subobj.test   5 Jul 2002 16:31:25 -0000
@@ -4,6 +4,8 @@
 
 . $srcdir/defs || exit 1
 
+set -e
+
 cat >> configure.in << 'END'
 AM_PROG_CC_C_O
 AC_PROG_CC
@@ -15,12 +17,14 @@
 wish_SOURCES = generic/a.c generic/b.c
 END
 
-: > compile
-
-$ACLOCAL || exit 1
-$AUTOMAKE || exit 1
+$ACLOCAL
+$AUTOMAKE --add-missing 2>stderr
+cat stderr
+# Make sure compile is installed, and that Automake says so.
+grep 'install.*compile' stderr
+test -f compile
 
-$FGREP 'generic/a.$(OBJEXT)' Makefile.in || exit 1
+$FGREP 'generic/a.$(OBJEXT)' Makefile.in
 grep '[^/]a\.\$(OBJEXT)' Makefile.in && exit 1
 
 # Opportunistically test for a different bug.
Index: tests/subobj2.test
===================================================================
RCS file: /cvs/automake/automake/tests/subobj2.test,v
retrieving revision 1.4
diff -u -r1.4 subobj2.test
--- tests/subobj2.test  30 May 2002 06:05:05 -0000      1.4
+++ tests/subobj2.test  5 Jul 2002 16:31:25 -0000
@@ -4,6 +4,8 @@
 
 . $srcdir/defs || exit 1
 
+set -e
+
 cat >> configure.in << 'END'
 AC_PROG_CXX
 END
@@ -14,11 +16,9 @@
 wish_SOURCES = generic/a.cc generic/b.cxx
 END
 
-: > compile
-
-$ACLOCAL || exit 1
-$AUTOMAKE || exit 1
+$ACLOCAL
+$AUTOMAKE
 
-$FGREP 'generic/a.$(OBJEXT)' Makefile.in || exit 1
+$FGREP 'generic/a.$(OBJEXT)' Makefile.in
 grep '[^/]a\.\$(OBJEXT)' Makefile.in && exit 1
 $FGREP -e '-c -o' Makefile.in

-- 
Alexandre Duret-Lutz




reply via email to

[Prev in Thread] Current Thread [Next in Thread]