* lib/Automake/Item.pm, lib/Automake/ItemDef.pm: New files.
authorAlexandre Duret-Lutz <adl@gnu.org>
Tue, 12 Aug 2003 17:52:09 +0000 (17:52 +0000)
committerAlexandre Duret-Lutz <adl@gnu.org>
Tue, 12 Aug 2003 17:52:09 +0000 (17:52 +0000)
* lib/Automake/Rule.pm, lib/Automake/RuleDef.pm: New files.
* lib/Automake/Makefile.am (dist_perllib_DATA): Add them.
* lib/Automake/VarDef.pm: Make this a subclass of Automake::ItemDef.
(new): Adjust to call Automake::ItemDef::new.
(comment, location, owner): Delete.  Now inherited from ItemDef.
* lib/Automake/Variable.pm: Make this a subclass of Automake::Item.
(_new): Adjust to call Automake::Item::new.
(name, def, rdef, _set, conditions, not_always_defined_in_cond):
Delete.  How inherited from Item, where `_set' is called `set'.
* automake.in (SUFFIX_RULE_PATTERN): Delete. Now in Automake::Rule.
(suffix_rules_default): Delete.  Now
Automake::Rule::_suffix_rules_default
(suffixes): Delete.  Now Automake::Rule::suffixes.
(TARGET_AUTOMAKE, TARGET_USER): Delete.  Now
Automake::RuleDef::RULE_AUTOMAKE and Automake::RuleDef::RULE_USER.
(%targets, %target_source, %target_name, %target_owner): Delete,
replaced by the Rule and RuleDef classes.
(dependencies, depend, actions): Delete.  Now in Automake::Rule.
(suffix_rules, register_suffix_rule): Likewise.
(KNOWN_EXTENSIONS_PATTERN, accept_extensions): Likewise.
(known_extensions_list): Delete.  Now
Automake::Rule::_known_extensions_list.
(target_conditions): Delete.  Now inherited by Automake::Rule
from Automake::Item::conditions.
(rule_define): Delete.  Now Automake::Rule::define.  Adjust all
callers.
(target_defined): Delete.  Now Automake::Rule::rule.  Adjust all
callers.
(initialize_per_input): Adjust to call Automake::Rule::reset.
(err_target, err_cond_target, msg_cond_target, msg_target,
reject_target): Delete.  Now defined in Automake::Rule as
err_rule, err_cond_rule, msg_cond_rule, msg_rule and reject_target.
Adjust all callers.
(handle_languages): Call suffix_rules_count.
* tests/location.test: Adjust expected diagnostics.  We now display
$(EXEEXT) accurately.

ChangeLog
automake.in
lib/Automake/Item.pm [new file with mode: 0644]
lib/Automake/ItemDef.pm [new file with mode: 0644]
lib/Automake/Makefile.am
lib/Automake/Makefile.in
lib/Automake/Rule.pm [new file with mode: 0644]
lib/Automake/RuleDef.pm [new file with mode: 0644]
lib/Automake/VarDef.pm
lib/Automake/Variable.pm
tests/location.test

index d98843c..216c2ce 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,43 @@
+2003-08-12  Alexandre Duret-Lutz  <adl@gnu.org>
+
+       * lib/Automake/Item.pm, lib/Automake/ItemDef.pm: New files.
+       * lib/Automake/Rule.pm, lib/Automake/RuleDef.pm: New files.
+       * lib/Automake/Makefile.am (dist_perllib_DATA): Add them.
+       * lib/Automake/VarDef.pm: Make this a subclass of Automake::ItemDef.
+       (new): Adjust to call Automake::ItemDef::new.
+       (comment, location, owner): Delete.  Now inherited from ItemDef.
+       * lib/Automake/Variable.pm: Make this a subclass of Automake::Item.
+       (_new): Adjust to call Automake::Item::new.
+       (name, def, rdef, _set, conditions, not_always_defined_in_cond):
+       Delete.  How inherited from Item, where `_set' is called `set'.
+       * automake.in (SUFFIX_RULE_PATTERN): Delete. Now in Automake::Rule.
+       (suffix_rules_default): Delete.  Now
+       Automake::Rule::_suffix_rules_default
+       (suffixes): Delete.  Now Automake::Rule::suffixes.
+       (TARGET_AUTOMAKE, TARGET_USER): Delete.  Now
+       Automake::RuleDef::RULE_AUTOMAKE and Automake::RuleDef::RULE_USER.
+       (%targets, %target_source, %target_name, %target_owner): Delete,
+       replaced by the Rule and RuleDef classes.
+       (dependencies, depend, actions): Delete.  Now in Automake::Rule.
+       (suffix_rules, register_suffix_rule): Likewise.
+       (KNOWN_EXTENSIONS_PATTERN, accept_extensions): Likewise.
+       (known_extensions_list): Delete.  Now
+       Automake::Rule::_known_extensions_list.
+       (target_conditions): Delete.  Now inherited by Automake::Rule
+       from Automake::Item::conditions.
+       (rule_define): Delete.  Now Automake::Rule::define.  Adjust all
+       callers.
+       (target_defined): Delete.  Now Automake::Rule::rule.  Adjust all
+       callers.
+       (initialize_per_input): Adjust to call Automake::Rule::reset.
+       (err_target, err_cond_target, msg_cond_target, msg_target,
+       reject_target): Delete.  Now defined in Automake::Rule as
+       err_rule, err_cond_rule, msg_cond_rule, msg_rule and reject_target.
+       Adjust all callers.
+       (handle_languages): Call suffix_rules_count.
+       * tests/location.test: Adjust expected diagnostics.  We now display
+       $(EXEEXT) accurately.
+
 2003-08-10  Alexandre Duret-Lutz  <adl@gnu.org>
 
        Revert the fix for PR automake/291:
index a3eb8d7..df61f28 100755 (executable)
@@ -135,9 +135,10 @@ use Automake::Options;
 use Automake::Version;
 use Automake::Variable;
 use Automake::VarDef;
+use Automake::Rule;
+use Automake::RuleDef;
 use Automake::Wrap 'makefile_wrap';
 use File::Basename;
-use Tie::RefHash;
 use Carp;
 
 ## ----------- ##
@@ -165,8 +166,6 @@ my $TARGET_PATTERN='[$a-zA-Z_.@%][-.a-zA-Z0-9_(){}/$+@%]*';
 my $RULE_PATTERN =
   "^($TARGET_PATTERN(?:(?:\\\\\n|\\s)+$TARGET_PATTERN)*) *:([^=].*|)\$";
 
-my $SUFFIX_RULE_PATTERN =
-    '^(\.[a-zA-Z0-9_(){}$+@]+)(\.[a-zA-Z0-9_(){}$+@]+)' . "\$";
 # Only recognize leading spaces, not leading tabs.  If we recognize
 # leading tabs here then we need to make the reader smarter, because
 # otherwise it will think rules like `foo=bar; \' are errors.
@@ -412,10 +411,6 @@ my %required_targets =
 # should distribute depcomp -- has been generated.)
 my $automake_needs_to_reprocess_all_files = 0;
 
-# Same as $suffix_rules (declared below), but records only the
-# default rules supplied by the languages Automake supports.
-my $suffix_rules_default;
-
 # If a file name appears as a key in this hash, then it has already
 # been checked for.  This variable is local to the "require file"
 # functions.
@@ -447,30 +442,6 @@ my $output_trailer;
 my $output_all;
 my $output_header;
 
-# Suffixes found during a run.
-my @suffixes;
-
-# This holds the names which are targets.  These also appear in
-# %contents.  $targets{TARGET}{COND} is the location of the definition
-# of TARGET for condition COND.  TARGETs should not include
-# a trailing $(EXEEXT), we record this in %target_name.
-my %targets;       tie %targets, 'Tie::RefHash::Nestable';
-
-# $target_source{TARGET}{COND} is the filename where TARGET
-# were defined for condition COND.  Note this must be a
-# filename, *without* any line number.
-my %target_source; tie %target_source, 'Tie::RefHash::Nestable';
-
-# $target_name{TARGET}{COND} is the real name of TARGET (in condition COND).
-# The real name is often TARGET or TARGET$(EXEEXT), and TARGET never
-# contain $(EXEEXT)
-my %target_name;   tie %target_name, 'Tie::RefHash::Nestable';
-
-# $target_owner{TARGET}{COND} the owner of TARGET in condition COND.
-my %target_owner;  tie %target_owner, 'Tie::RefHash::Nestable';
-use constant TARGET_AUTOMAKE => 0; # Target defined by Automake.
-use constant TARGET_USER => 1; # Target defined in the user's Makefile.am.
-
 # This is the conditional stack, updated on if/else/endif, and
 # used to build Condition objects.
 my @cond_stack;
@@ -488,27 +459,6 @@ my @all;
 my @check;
 my @check_tests;
 
-# Holds the dependencies of targets which dependencies are factored.
-# Typically, `.PHONY' will appear in plenty of *.am files, but must
-# be output once.  Arguably all pure dependencies could be subject
-# to this factorization, but it is not unpleasant to have paragraphs
-# in Makefile: keeping related stuff altogether.
-my %dependencies;
-
-# &depend ($CATEGORY, @DEPENDENDEES)
-# ----------------------------------
-# The target $CATEGORY depends on @DEPENDENDEES.
-sub depend ($@)
-{
-  my ($category, @dependendees) = @_;
-  push (@{$dependencies{$category}}, @dependendees);
-}
-
-
-# Holds the factored actions.  Tied to %DEPENDENCIES, i.e., filled
-# only when keys exists in %DEPENDENCIES.
-my %actions;
-
 # Keys in this hash table are files to delete.  The associated
 # value tells when this should happen (MOSTLY_CLEAN, DIST_CLEAN, etc.)
 my %clean_files;
@@ -556,27 +506,6 @@ my @dist_targets;
 # trailing `/'.
 my %de_ansi_files;
 
-# This maps the source extension for all suffix rule seen to
-# a \hash whose keys are the possible output extensions.
-#
-# Note that this is transitively closed by construction:
-# if we have
-#       exists $suffix_rules{$ext1}{$ext2}
-#    && exists $suffix_rules{$ext2}{$ext3}
-# then we also have
-#       exists $suffix_rules{$ext1}{$ext3}
-#
-# So it's easy to check whether '.foo' can be transformed to '.$(OBJEXT)'
-# by checking whether $suffix_rules{'.foo'}{'.$(OBJEXT)'} exist.  This
-# will work even if transforming '.foo' to '.$(OBJEXT)' involves a chain
-# of several suffix rules.
-#
-# The value of `$suffix_rules{$ext1}{$ext2}' is the a pair
-# `[ $next_sfx, $dist ]' where `$next_sfx' is target suffix
-# for the next rule to use to reach '$ext2', and `$dist' the
-# distance to `$ext2'.
-my $suffix_rules;
-
 # This is the name of the redirect `all' target to use.
 my $all_target;
 
@@ -609,32 +538,14 @@ my $get_object_extension_was_run;
 
 ################################################################
 
-# Pattern that matches all know input extensions (i.e. extensions used
-# by the languages supported by Automake).  Using this pattern
-# (instead of `\..*$') to match extensions allows Automake to support
-# dot-less extensions.
-my $KNOWN_EXTENSIONS_PATTERN = "";
-my @known_extensions_list = ();
-
-# accept_extensions (@EXTS)
-# -------------------------
-# Update $KNOWN_EXTENSIONS_PATTERN to recognize the extensions
-# listed @EXTS.  Extensions should contain a dot if needed.
-sub accept_extensions (@)
-{
-    push @known_extensions_list, @_;
-    $KNOWN_EXTENSIONS_PATTERN =
-       '(?:' . join ('|', map (quotemeta, @known_extensions_list)) . ')';
-}
-
 # var_SUFFIXES_trigger ($TYPE, $VALUE)
 # ------------------------------------
-# This is called automagically by macro_define() when SUFFIXES
+# This is called by Automake::Variable::define() when SUFFIXES
 # is defined ($TYPE eq '') or appended ($TYPE eq '+').
 # The work here needs to be performed as a side-effect of the
 # macro_define() call because SUFFIXES definitions impact
-# on $KNOWN_EXTENSIONS_PATTERN, and $KNOWN_EXTENSIONS_PATTERN
-# are used when parsing the input am file.
+# on $KNOWN_EXTENSIONS_PATTERN which is used used when parsing
+# the input am file.
 sub var_SUFFIXES_trigger ($$)
 {
     my ($type, $value) = @_;
@@ -671,15 +582,9 @@ sub initialize_per_input ()
     $output_all = '';
     $output_header = '';
 
-    @suffixes = ();
-
     Automake::Options::reset;
     Automake::Variable::reset;
-
-    %targets = ();
-    %target_source = ();
-    %target_name = ();
-    %target_owner = ();
+    Automake::Rule::reset;
 
     @cond_stack = ();
 
@@ -691,52 +596,6 @@ sub initialize_per_input ()
     @check = ();
     @check_tests = ();
 
-    %dependencies =
-      (
-       # Texinfoing.
-       'dvi'      => [],
-       'dvi-am'   => [],
-       'pdf'      => [],
-       'pdf-am'   => [],
-       'ps'       => [],
-       'ps-am'    => [],
-       'info'     => [],
-       'info-am'  => [],
-       'html'     => [],
-       'html-am'  => [],
-
-       # Installing/uninstalling.
-       'install-data-am'      => [],
-       'install-exec-am'      => [],
-       'uninstall-am'         => [],
-
-       'install-man'         => [],
-       'uninstall-man'       => [],
-
-       'install-info'         => [],
-       'install-info-am'      => [],
-       'uninstall-info'       => [],
-
-       'installcheck-am'      => [],
-
-       # Cleaning.
-       'clean-am'             => [],
-       'mostlyclean-am'       => [],
-       'maintainer-clean-am'  => [],
-       'distclean-am'         => [],
-       'clean'                => [],
-       'mostlyclean'          => [],
-       'maintainer-clean'     => [],
-       'distclean'            => [],
-
-       # Tarballing.
-       'dist-all'             => [],
-
-       # Phoning.
-       '.PHONY'               => []
-      );
-    %actions = ();
-
     %clean_files = ();
 
     @sources = ();
@@ -753,18 +612,6 @@ sub initialize_per_input ()
 
     %de_ansi_files = ();
 
-
-    # The first time we initialize the variables,
-    # we save the value of $suffix_rules.
-    if (defined $suffix_rules_default)
-      {
-       $suffix_rules = $suffix_rules_default;
-      }
-    else
-      {
-       $suffix_rules_default = $suffix_rules;
-      }
-
     $all_target = '';
 
     %extension_seen = ();
@@ -1002,22 +849,6 @@ register_language ('name' => 'java',
 
 # Error reporting functions.
 
-# err_target ($TARGETNAME, $MESSAGE, [%OPTIONS])
-# ----------------------------------------------
-# Uncategorized errors about targets.
-sub err_target ($$;%)
-{
-  msg_target ('error', @_);
-}
-
-# err_cond_target ($COND, $TARGETNAME, $MESSAGE, [%OPTIONS])
-# ----------------------------------------------------------
-# Uncategorized errors about conditional targets.
-sub err_cond_target ($$$;%)
-{
-  msg_cond_target ('error', @_);
-}
-
 # err_am ($MESSAGE, [%OPTIONS])
 # -----------------------------
 # Uncategorized errors about the current Makefile.am.
@@ -1034,26 +865,6 @@ sub err_ac ($;%)
   msg_ac ('error', @_);
 }
 
-# msg_cond_target ($CHANNEL, $COND, $TARGETNAME, $MESSAGE, [%OPTIONS])
-# --------------------------------------------------------------------
-# Messages about conditional targets.
-sub msg_cond_target ($$$$;%)
-{
-  my ($channel, $cond, $target, $msg, %opts) = @_;
-  msg $channel, $targets{$target}{$cond}, $msg, %opts;
-}
-
-# msg_target ($CHANNEL, $TARGETNAME, $MESSAGE, [%OPTIONS])
-# --------------------------------------------------------
-# Messages about targets.
-sub msg_target ($$$;%)
-{
-  my ($channel, $target, $msg, %opts) = @_;
-  # Don't know which condition is concerned.  Pick any.
-  my $cond = target_conditions ($target)->one_cond;
-  msg_cond_target ($channel, $cond, $target, $msg, %opts);
-}
-
 # msg_am ($CHANNEL, $MESSAGE, [%OPTIONS])
 # ---------------------------------------
 # Messages about about the current Makefile.am.
@@ -1072,20 +883,6 @@ sub msg_ac ($$;%)
   msg $channel, $configure_ac, $msg, %opts;
 }
 
-# $BOOL
-# reject_target ($VAR, $ERROR_MSG)
-# --------------------------------
-sub reject_target ($$)
-{
-  my ($target, $msg) = @_;
-  if (target_defined ($target))
-    {
-      err_target $target, $msg;
-      return 1;
-    }
-  return 0;
-}
-
 ################################################################
 
 # subst ($TEXT)
@@ -1515,8 +1312,7 @@ sub handle_languages
     # suffix rule was learned), don't bother with the C stuff.  But if
     # anything else creeps in, then use it.
     $needs_c = 1
-      if $need_link || ((scalar keys %$suffix_rules)
-                       - (scalar keys %$suffix_rules_default)) > 1;
+      if $need_link || suffix_rules_count > 1;
 
     if ($needs_c)
       {
@@ -3274,7 +3070,7 @@ sub handle_dist
     {
       if (-f ($relative_dir . "/" . $cfile)
          # The file might be absent, but if it can be built it's ok.
-         || exists $targets{$cfile})
+         || rule $cfile)
        {
          &push_dist_common ($cfile);
        }
@@ -3349,7 +3145,7 @@ sub handle_dist
     }
 
   # Rule to check whether a distribution is viable.
-  my %transform = ('DISTCHECK-HOOK' => &target_defined ('distcheck-hook'),
+  my %transform = ('DISTCHECK-HOOK' => !! rule 'distcheck-hook',
                   'GETTEXT' => $seen_gettext && !$seen_gettext_external);
 
   # Prepend $(distdir) to each directory given.
@@ -3399,7 +3195,7 @@ sub handle_dist
   # allows users to do random weird things to the distribution
   # before it is packaged up.
   push (@dist_targets, 'dist-hook')
-    if &target_defined ('dist-hook');
+    if rule 'dist-hook';
   $transform{'DIST-TARGETS'} = join(' ', @dist_targets);
 
   $output_rules .= &file_contents ('distdir',
@@ -3918,17 +3714,18 @@ sub handle_footer
     $output_vars .= 'SOURCES = ' . variable_value ('SOURCES') . "\n\n"
       if variable_value ('SOURCES');
 
-    reject_target ('.SUFFIXES',
-                  "use variable `SUFFIXES', not target `.SUFFIXES'");
+    reject_rule ('.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
     # anything else, by sticking it right after the default: target.
     $output_header .= ".SUFFIXES:\n";
     my $suffixes = var 'SUFFIXES';
+    my @suffixes = Automake::Rule::suffixes;
     if (@suffixes || $suffixes)
     {
-       # Make sure suffixes has unique elements.  Sort them to ensure
+       # Make sure SUFFIXES has unique elements.  Sort them to ensure
        # the output remains consistent.  However, $(SUFFIXES) is
        # always at the start of the list, unsorted.  This is done
        # because make will choose rules depending on the ordering of
@@ -3961,7 +3758,7 @@ sub handle_install ()
                             ? (" \$(BUILT_SOURCES)\n"
                                . "\t\$(MAKE) \$(AM_MAKEFLAGS)")
                             : ''),
-     'installdirs-local' => (target_defined ('installdirs-local')
+     'installdirs-local' => (rule 'installdirs-local'
                             ? ' installdirs-local' : ''),
      am__installdirs => variable_value ('am__installdirs') || '');
 }
@@ -3987,7 +3784,7 @@ sub handle_all ($)
       }
 
     # Install `all' hooks.
-    if (&target_defined ("all-local"))
+    if (rule "all-local")
     {
       push (@all, "all-local");
       &depend ('.PHONY', "all-local");
@@ -4036,7 +3833,7 @@ sub handle_all ($)
 # Handle check merge target specially.
 sub do_check_merge_target ()
 {
-  if (target_defined ('check-local'))
+  if (rule 'check-local')
     {
       # User defined local form of target.  So include it.
       push @check_tests, 'check-local';
@@ -4146,16 +3943,16 @@ sub handle_factored_dependencies
     {
       my $x = $utarg;
       $x =~ s/(data|exec)-//;
-      reject_target ($utarg, "use `$x', not `$utarg'");
+      reject_rule ($utarg, "use `$x', not `$utarg'");
     }
 
-  reject_target ('install-local',
-                "use `install-data-local' or `install-exec-local', "
-                . "not `install-local'");
+  reject_rule ('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")
+  reject_rule ('install-info-local',
+              "`install-info-local' target defined but "
+              . "`no-installinfo' option not in use")
     unless option 'no-installinfo';
 
   # Install the -local hooks.
@@ -4163,10 +3960,10 @@ sub handle_factored_dependencies
     {
       # Hooks are installed on the -am targets.
       s/-am$// or next;
-      if (&target_defined ("$_-local"))
+      if (rule "$_-local")
        {
          depend ("$_-am", "$_-local");
-         &depend ('.PHONY', "$_-local");
+         depend ('.PHONY', "$_-local");
        }
     }
 
@@ -4174,7 +3971,7 @@ sub handle_factored_dependencies
   # FIXME: Why not be as liberal as we are with -local hooks?
   foreach ('install-exec', 'install-data', 'uninstall')
     {
-      if (&target_defined ("$_-hook"))
+      if (rule ("$_-hook"))
        {
          $actions{"$_-am"} .=
            ("\t\@\$(NORMAL_INSTALL)\n"
@@ -4205,7 +4002,8 @@ sub handle_factored_dependencies
       if ($_ ne '.PHONY')
        {
          @undefined_conds =
-           rule_define ($_, 'internal', TARGET_AUTOMAKE, TRUE, INTERNAL);
+           Automake::Rule::define ($_, 'internal',
+                                   RULE_AUTOMAKE, TRUE, INTERNAL);
        }
       my @uniq_deps = uniq (sort @{$dependencies{$_}});
       foreach my $cond (@undefined_conds)
@@ -5120,7 +4918,7 @@ sub register_language (%)
     {
       foreach my $dest (&{$lang->output_extensions} ($suffix))
        {
-         &register_suffix_rule ('internal', $suffix, $dest);
+         register_suffix_rule (INTERNAL, $suffix, $dest);
        }
     }
 }
@@ -5260,18 +5058,6 @@ sub cond_stack_endif ($$$)
 ## ------------------------ ##
 
 
-# @CONDS
-# target_conditions ($TARGET)
-# ---------------------------
-# Get the list of conditions that a target is defined with.
-sub target_conditions ($)
-{
-    my ($target) = @_;
-    my @conds = keys %{$targets{$target}};
-    return new Automake::DisjConditions @conds;
-}
-
-
 # &define_pretty_variable ($VAR, $COND, $WHERE, @VALUE)
 # -----------------------------------------------------
 # Like define_variable, but the value is a list, and the variable may
@@ -5375,350 +5161,6 @@ sub define_linker_variable ($)
 
 ################################################################
 
-## ---------------- ##
-## Handling rules.  ##
-## ---------------- ##
-
-sub register_suffix_rule ($$$)
-{
-  my ($where, $src, $dest) = @_;
-
-  verb "Sources ending in $src become $dest";
-  push @suffixes, $src, $dest;
-
-  # When tranforming sources to objects, Automake uses the
-  # %suffix_rules to move from each source extension to
-  # `.$(OBJEXT)', not to `.o' or `.obj'.  However some people
-  # define suffix rules for `.o' or `.obj', so internally we will
-  # consider these extensions equivalent to `.$(OBJEXT)'.  We
-  # CANNOT rewrite the target (i.e., automagically replace `.o'
-  # and `.obj' by `.$(OBJEXT)' in the output), or warn the user
-  # that (s)he'd better use `.$(OBJEXT)', because Automake itself
-  # output suffix rules for `.o' or `.obj'...
-  $dest = '.$(OBJEXT)' if ($dest eq '.o' || $dest eq '.obj');
-
-  # Reading the comments near the declaration of $suffix_rules might
-  # help to understand the update of $suffix_rules that follows...
-
-  # Register $dest as a possible destination from $src.
-  # We might have the create the \hash.
-  if (exists $suffix_rules->{$src})
-    {
-      $suffix_rules->{$src}{$dest} = [ $dest, 1 ];
-    }
-  else
-    {
-      $suffix_rules->{$src} = { $dest => [ $dest, 1 ] };
-    }
-
-  # If we know how to transform $dest in something else, then
-  # we know how to transform $src in that "something else".
-  if (exists $suffix_rules->{$dest})
-    {
-      for my $dest2 (keys %{$suffix_rules->{$dest}})
-       {
-         my $dist = $suffix_rules->{$dest}{$dest2}[1] + 1;
-         # Overwrite an existing $src->$dest2 path only if
-         # the path via $dest which is shorter.
-         if (! exists $suffix_rules->{$src}{$dest2}
-             || $suffix_rules->{$src}{$dest2}[1] > $dist)
-           {
-             $suffix_rules->{$src}{$dest2} = [ $dest, $dist ];
-           }
-       }
-    }
-
-  # Similarly, any extension that can be derived into $src
-  # can be derived into the same extenstions as $src can.
-  my @dest2 = keys %{$suffix_rules->{$src}};
-  for my $src2 (keys %$suffix_rules)
-    {
-      if (exists $suffix_rules->{$src2}{$src})
-       {
-         for my $dest2 (@dest2)
-           {
-             my $dist = $suffix_rules->{$src}{$dest2} + 1;
-             # Overwrite an existing $src2->$dest2 path only if
-             # the path via $src is shorter.
-             if (! exists $suffix_rules->{$src2}{$dest2}
-                 || $suffix_rules->{$src2}{$dest2}[1] > $dist)
-               {
-                 $suffix_rules->{$src2}{$dest2} = [ $src, $dist ];
-               }
-           }
-       }
-    }
-}
-
-# @CONDS
-# rule_define ($TARGET, $SOURCE, $OWNER, $COND, $WHERE)
-# -----------------------------------------------------
-# Define a new rule.  $TARGET is the rule name.  $SOURCE
-# is the filename the rule comes from.  $OWNER is the
-# owner of the rule (TARGET_AUTOMAKE or TARGET_USER).
-# $COND is the Condition under which the rule is defined.
-# $WHERE is the Location where the rule is defined.
-# Returns a (possibly empty) list of Conditions where the rule
-# should be defined.
-sub rule_define ($$$$$)
-{
-  my ($target, $source, $owner, $cond, $where) = @_;
-
-  prog_error "$where is not a reference"
-    unless ref $where;
-  prog_error "$cond is not a reference"
-    unless ref $cond;
-
-  # Don't even think about defining a rule in condition FALSE.
-  return () if $cond == FALSE;
-
-  # For now `foo:' will override `foo$(EXEEXT):'.  This is temporary,
-  # though, so we emit a warning.
-  (my $noexe = $target) =~ s,\$\(EXEEXT\)$,,;
-  if ($noexe ne $target
-      && exists $targets{$noexe}
-      && exists $targets{$noexe}{$cond}
-      && $target_name{$noexe}{$cond} ne $target)
-    {
-      # The no-exeext option enables this feature.
-      if (! option 'no-exeext')
-       {
-         msg ('obsolete', $targets{$noexe}{$cond},
-              "deprecated feature: target `$noexe' overrides "
-              . "`$noexe\$(EXEEXT)'\n"
-              . "change your target to read `$noexe\$(EXEEXT)'");
-         msg ('obsolete', $where, "target `$target' was defined here");
-       }
-      # Don't `return ()' now, as this might hide target clashes
-      # detected below.
-    }
-
-  # For now on, strip off $(EXEEXT) from $target, so we can diagnose
-  # a clash if `ctags$(EXEEXT):' is redefined after `ctags:'.
-  my $realtarget = $target;
-  $target = $noexe;
-
-  # A GNU make-style pattern rule has a single "%" in the target name.
-  msg ('portability', $where,
-       "`%'-style pattern rules are a GNU make extension")
-    if $target =~ /^[^%]*%[^%]*$/;
-
-  # Diagnose target redefinitions.
-  if (exists $target_source{$target}{$cond})
-    {
-      # Sanity checks.
-      prog_error ("\$target_source{$target}{$cond} exists, but \$target_owner"
-                 . " doesn't.")
-       unless exists $target_owner{$target}{$cond};
-      prog_error ("\$target_source{$target}{$cond} exists, but \$targets"
-                 . " doesn't.")
-       unless exists $targets{$target}{$cond};
-      prog_error ("\$target_source{$target}{$cond} exists, but \$target_name"
-                 . " doesn't.")
-       unless exists $target_name{$target}{$cond};
-
-      my $oldowner  = $target_owner{$target}{$cond};
-
-      # Don't mention true conditions in diagnostics.
-      my $condmsg =
-       $cond == TRUE ? '' : " in condition `" . $cond->human . "'";
-
-      if ($owner == TARGET_USER)
-       {
-         if ($oldowner == TARGET_USER)
-           {
-             # Ignore `%'-style pattern rules.  We'd need the
-             # dependencies to detect duplicates, and they are
-             # already diagnosed as unportable by -Wportability.
-             if ($target !~ /^[^%]*%[^%]*$/)
-               {
-                 ## FIXME: Presently we can't diagnose duplcate user rules
-                 ## because we doesn't distinguish rules with commands
-                 ## from rules that only add dependencies.  E.g.,
-                 ##   .PHONY: foo
-                 ##   .PHONY: bar
-                 ## is legitimate. (This is phony.test.)
-
-                 # msg ('syntax', $where,
-                 #      "redefinition of `$target'$condmsg...", partial => 1);
-                 # msg_cond_target ('syntax', $cond, $target,
-                 #                "... `$target' previously defined here");
-               }
-             # Return so we don't redefine the rule in our tables,
-             # don't check for ambiguous condition, etc.  The rule
-             # will be output anyway beauce &read_am_file ignore the
-             # return code.
-             return ();
-           }
-         else
-           {
-             # Since we parse the user Makefile.am before reading
-             # the Automake fragments, this condition should never happen.
-             prog_error ("user target `$target' seen after Automake's "
-                         . "definition\nfrom `$targets{$target}$condmsg'");
-           }
-       }
-      else # $owner == TARGET_AUTOMAKE
-       {
-         if ($oldowner == TARGET_USER)
-           {
-             # -am targets listed in %dependencies support a -local
-             # variant.  If the user tries to override TARGET or
-             # TARGET-am for which there exists a -local variant,
-             # just tell the user to use it.
-             my $hint = 0;
-             my $noam = $target;
-             $noam =~ s/-am$//;
-             if (exists $dependencies{"$noam-am"})
-               {
-                 $hint = "consider using $target-local instead of $target";
-               }
-
-             msg_cond_target ('override', $cond, $target,
-                              "user target `$target' defined here"
-                              . "$condmsg...", partial => 1);
-             msg ('override', $where,
-                  "... overrides Automake target `$target' defined here",
-                  partial => $hint);
-             msg_cond_target ('override', $cond, $target, $hint)
-               if $hint;
-
-             # Don't overwrite the user definition of TARGET.
-             return ();
-           }
-         else # $oldowner == TARGET_AUTOMAKE
-           {
-             # Automake should ignore redefinitions of its own
-             # rules if they came from the same file.  This makes
-             # it easier to process a Makefile fragment several times.
-             # Hower it's an error if the target is defined in many
-             # files.  E.g., the user might be using bin_PROGRAMS = ctags
-             # which clashes with our `ctags' rule.
-             # (It would be more accurate if we had a way to compare
-             # the *content* of both rules.  Then $targets_source would
-             # be useless.)
-             my $oldsource = $target_source{$target}{$cond};
-             return () if $source eq $oldsource;
-
-             msg ('syntax', $where, "redefinition of `$target'$condmsg...",
-                  partial => 1);
-             msg_cond_target ('syntax', $cond, $target,
-                              "... `$target' previously defined here");
-             return ();
-           }
-       }
-      # Never reached.
-      prog_error ("Unreachable place reached.");
-    }
-
-  # Conditions for which the rule should be defined.
-  my @conds = $cond;
-
-  # Check ambiguous conditional definitions.
-  my ($message, $ambig_cond) =
-    target_conditions ($target)->ambiguous_p ($target, $cond);
-  if ($message)                        # We have an ambiguty.
-    {
-      if ($owner == TARGET_USER)
-       {
-         # For user rules, just diagnose the ambiguity.
-         msg 'syntax', $where, "$message ...", partial => 1;
-         msg_cond_target ('syntax', $ambig_cond, $target,
-                          "... `$target' previously defined here");
-         return ();
-       }
-      else
-       {
-         # FIXME: for Automake rules, we can't diagnose ambiguities yet.
-         # The point is that Automake doesn't propagate conditions
-         # everywhere.  For instance &handle_PROGRAMS doesn't care if
-         # bin_PROGRAMS was defined conditionally or not.
-         # On the following input
-         #   if COND1
-         #   foo:
-         #           ...
-         #   else
-         #   bin_PROGRAMS = foo
-         #   endif
-         # &handle_PROGRAMS will attempt to define a `foo:' rule
-         # in condition TRUE (which conflicts with COND1).  Fixing
-         # this in &handle_PROGRAMS and siblings seems hard: you'd
-         # have to explain &file_contents what to do with a
-         # condition.  So for now we do our best *here*.  If `foo:'
-         # was already defined in condition COND1 and we want to define
-         # it in condition TRUE, then define it only in condition !COND1.
-         # (See cond14.test and cond15.test for some test cases.)
-         my $defined_conds = target_conditions ($target);
-         @conds = ();
-         for my $undefined_cond ($defined_conds->invert->conds)
-           {
-             push @conds, $cond->merge ($undefined_cond);
-           }
-         # No conditions left to define the rule.
-         # Warn, because our workaround is meaningless in this case.
-         if (scalar @conds == 0)
-           {
-             msg 'syntax', $where, "$message ...", partial => 1;
-             msg_cond_target ('syntax', $ambig_cond, $target,
-                              "... `$target' previously defined here");
-             return ();
-           }
-       }
-    }
-
-  # Finally define this rule.
-  for my $c (@conds)
-    {
-      $targets{$target}{$c} = $where->clone;
-      $target_source{$target}{$c} = $source;
-      $target_owner{$target}{$c} = $owner;
-      $target_name{$target}{$c} = $realtarget;
-    }
-
-  # We honor inference rules with multiple targets because many
-  # make support this and people use it.  However this is disallowed
-  # by POSIX.  We'll print a warning later.
-  my $target_count = 0;
-  my $inference_rule_count = 0;
-  for my $t (split (' ', $target))
-    {
-      ++$target_count;
-      # Check the rule for being a suffix rule. If so, store in a hash.
-      # Either it's a rule for two known extensions...
-      if ($t =~ /^($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).
-         || ($t =~ /$SUFFIX_RULE_PATTERN/o && accept_extensions($1)))
-       {
-         ++$inference_rule_count;
-         register_suffix_rule ($where, $1, $2);
-       }
-    }
-
-  # POSIX allow multiple targets befor the colon, but disallow
-  # definitions of multiple Inference rules.  It's also
-  # disallowed to mix plain targets with inference rules.
-  msg ('portability', $where,
-       "Inference rules can have only one target before the colon (POSIX).")
-    if $inference_rule_count > 0 && $target_count > 1;
-
-  return @conds;
-}
-
-
-# See if a target exists.
-sub target_defined
-{
-    my ($target) = @_;
-    return exists $targets{$target};
-}
-
-
-################################################################
-
 # &check_trailing_slash ($WHERE, $LINE)
 # --------------------------------------
 # Return 1 iff $LINE ends with a slash.
@@ -5904,7 +5346,7 @@ sub read_am_file ($$)
            # For now we have to output all definitions of user rules
            # and can't diagnose duplicates (see the comment in
            # rule_define). So we go on and ignore the return value.
-           rule_define ($1, $amfile, TARGET_USER, $cond, $where);
+           Automake::Rule::define ($1, $amfile, RULE_USER, $cond, $where);
 
            check_variable_expansions ($_, $where);
 
@@ -6295,9 +5737,9 @@ sub file_contents_internal ($$$%)
                  # Free-lance dependency.  Output the rule for all the
                  # targets instead of one by one.
                  my @undefined_conds =
-                   rule_define ($targets, $file,
-                                $is_am ? TARGET_AUTOMAKE : TARGET_USER,
-                                $cond, $where);
+                   Automake::Rule::define ($targets, $file,
+                                           $is_am ? RULE_AUTOMAKE : RULE_USER,
+                                           $cond, $where);
                  for my $undefined_cond (@undefined_conds)
                    {
                      my $condparagraph = $paragraph;
@@ -6903,7 +6345,7 @@ sub require_file_internal ($$@)
                # the Makefile, don't print anything.  This allows files
                # like README, AUTHORS, or THANKS to be generated.
                next
-                 if !$suppress && target_defined ($file);
+                 if !$suppress && rule $file;
 
                msg ($suppress ? 'note' : 'error', $where, "$message$trailer");
            }
diff --git a/lib/Automake/Item.pm b/lib/Automake/Item.pm
new file mode 100644 (file)
index 0000000..f95154c
--- /dev/null
@@ -0,0 +1,205 @@
+# Copyright (C) 2003  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::Item;
+use strict;
+use Carp;
+
+use Automake::ChannelDefs;
+use Automake::DisjConditions;
+
+=head1 NAME
+
+Automake::Item - base class for Automake::Variable and Automake::Rule
+
+=head1 DESCRIPTION
+
+=head2 Methods
+
+=over 4
+
+=item C<new Automake::Item $name>
+
+Create and return an empty Item called C<$name>.
+
+=cut
+
+sub new ($$)
+{
+  my ($class, $name) = @_;
+  my $self = {
+    name => $name,
+    defs => {},
+    conds => {},
+  };
+  bless $self, $class;
+  return $self;
+}
+
+=item C<$item-E<gt>name>
+
+Return the name of C<$item>.
+
+=cut
+
+sub name ($)
+{
+  my ($self) = @_;
+  return $self->{'name'};
+}
+
+=item C<$item-E<gt>def ($cond)>
+
+Return the definition for this item in condition C<$cond>, if it
+exists.  Return 0 otherwise.
+
+=cut
+
+sub def ($$)
+{
+  my ($self, $cond) = @_;
+  return $self->{'defs'}{$cond} if exists $self->{'defs'}{$cond};
+  return 0;
+}
+
+=item C<$item-E<gt>rdef ($cond)>
+
+Return the definition for this item in condition C<$cond>.  Abort with
+an internal error if the item was not defined under this condition.
+
+The I<r> in front of C<def> stands for I<required>.  One
+should call C<rdef> to assert the conditional definition's existence.
+
+=cut
+
+sub rdef ($$)
+{
+  my ($self, $cond) = @_;
+  my $d = $self->def ($cond);
+  prog_error ("undefined condition `" . $cond->human . "' for `"
+             . $self->name . "'\n" . $self->dump)
+    unless $d;
+  return $d;
+}
+
+=item C<$item-E<gt>set ($cond, $def)>
+
+Add a new definition to an existing item.
+
+=cut
+
+sub set ($$$)
+{
+  my ($self, $cond, $def) = @_;
+  $self->{'defs'}{$cond} = $def;
+  $self->{'conds'}{$cond} = $cond;
+}
+
+=item C<$var-E<gt>conditions>
+
+Return an L<Automake::DisjConditions> describing the conditions that
+that an item is defined in.
+
+These are all the conditions for which is would be safe to call
+C<rdef>.
+
+=cut
+
+sub conditions ($)
+{
+  my ($self) = @_;
+  prog_error ("self is not a reference")
+    unless ref $self;
+  return new Automake::DisjConditions (values %{$self->{'conds'}});
+}
+
+=item C<@missing_conds = $var-E<gt>not_always_defined_in_cond ($cond)>
+
+Check whether C<$var> is always defined for condition C<$cond>.
+Return a list of conditions where the definition is missing.
+
+For instance, given
+
+  if COND1
+    if COND2
+      A = foo
+      D = d1
+    else
+      A = bar
+      D = d2
+    endif
+  else
+    D = d3
+  endif
+  if COND3
+    A = baz
+    B = mumble
+  endif
+  C = mumble
+
+we should have (we display result as conditional strings in this
+illustration, but we really return DisjConditions objects):
+
+  var ('A')->not_always_defined_in_cond ('COND1_TRUE COND2_TRUE')
+    => ()
+  var ('A')->not_always_defined_in_cond ('COND1_TRUE')
+    => ()
+  var ('A')->not_always_defined_in_cond ('TRUE')
+    => ("COND1_FALSE COND3_FALSE")
+  var ('B')->not_always_defined_in_cond ('COND1_TRUE')
+    => ("COND1_TRUE COND3_FALSE")
+  var ('C')->not_always_defined_in_cond ('COND1_TRUE')
+    => ()
+  var ('D')->not_always_defined_in_cond ('TRUE')
+    => ()
+  var ('Z')->not_always_defined_in_cond ('TRUE')
+    => ("TRUE")
+
+=cut
+
+sub not_always_defined_in_cond ($$)
+{
+  my ($self, $cond) = @_;
+
+  # Compute the subconditions where $var isn't defined.
+  return
+    $self->conditions
+      ->sub_conditions ($cond)
+       ->invert
+         ->simplify
+           ->multiply ($cond);
+}
+
+
+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:
diff --git a/lib/Automake/ItemDef.pm b/lib/Automake/ItemDef.pm
new file mode 100644 (file)
index 0000000..3d9361d
--- /dev/null
@@ -0,0 +1,113 @@
+# Copyright (C) 2003  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::ItemDef;
+use strict;
+use Carp;
+
+=head1 NAME
+
+Automake::ItemDef - base class for Automake::VarDef and Automake::RuleDef
+
+=head1 DESCRIPTION
+
+=head2 Methods
+
+=over 4
+
+=item C<my $def = Automake::new ($comment, $location, $owner)>
+
+Create a new Makefile-item definition.
+
+C<$comment> is any comment preceding the definition.  (Because
+Automake reorders items in the output, it also tries to carry comments
+around.)
+
+C<$location> is the place where the definition occured, it should be
+an instance of L<Automake::Location>.
+
+C<$owner> specifies who owns the rule.
+
+=cut
+
+sub new ($$$$)
+{
+  my ($class, $comment, $location, $owner) = @_;
+
+  my $self = {
+    comment => $comment,
+    location => $location,
+    owner => $owner,
+  };
+  bless $self, $class;
+
+  return $self;
+}
+
+=item C<$def-E<gt>comment>
+
+=item C<$def-E<gt>location>
+
+=item C<$def-E<gt>owner>
+
+Accessors to the various constituents of an C<ItemDef>.  See the
+documentation of C<new>'s arguments for a description of these.
+
+=cut
+
+sub comment ($)
+{
+  my ($self) = @_;
+  return $self->{'comment'};
+}
+
+sub location ($)
+{
+  my ($self) = @_;
+  return $self->{'location'};
+}
+
+sub owner ($)
+{
+  my ($self) = @_;
+  return $self->{'owner'};
+}
+
+=head1 SEE ALSO
+
+L<Automake::VarDef>, and L<Automake::RuleDef>.
+
+=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 2d3ed5d..1a950c1 100644 (file)
@@ -28,8 +28,12 @@ dist_perllib_DATA = \
   DisjConditions.pm \
   FileUtils.pm \
   General.pm \
+  Item.pm \
+  ItemDef.pm \
   Location.pm \
   Options.pm \
+  Rule.pm \
+  RuleDef.pm \
   Struct.pm \
   Variable.pm \
   VarDef.pm \
index 6b24e85..edbf7de 100644 (file)
@@ -138,8 +138,12 @@ dist_perllib_DATA = \
   DisjConditions.pm \
   FileUtils.pm \
   General.pm \
+  Item.pm \
+  ItemDef.pm \
   Location.pm \
   Options.pm \
+  Rule.pm \
+  RuleDef.pm \
   Struct.pm \
   Variable.pm \
   VarDef.pm \
diff --git a/lib/Automake/Rule.pm b/lib/Automake/Rule.pm
new file mode 100644 (file)
index 0000000..35fc11d
--- /dev/null
@@ -0,0 +1,844 @@
+# Copyright (C) 2003  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::Rule;
+use strict;
+use Carp;
+
+use Automake::Item;
+use Automake::RuleDef;
+use Automake::ChannelDefs;
+use Automake::Channels;
+use Automake::Options;
+use Automake::Condition qw (TRUE FALSE);
+use Automake::DisjConditions;
+require Exporter;
+use vars '@ISA', '@EXPORT', '@EXPORT_OK';
+@ISA = qw/Automake::Item Exporter/;
+@EXPORT = qw (reset register_suffix_rule suffix_rules_count
+             suffixes rules $suffix_rules $KNOWN_EXTENSIONS_PATTERN
+             depend %dependencies %actions accept_extensions
+             reject_rule msg_rule msg_cond_rule err_rule err_cond_rule
+             rule rrule ruledef rruledef);
+
+=head1 NAME
+
+Automake::Rule - support for rules definitions
+
+=head1 SYNOPSIS
+
+  use Automake::Rule;
+  use Automake::RuleDef;
+
+
+=head1 DESCRIPTION
+
+This package provides support for Makefile rule definitions.
+
+An C<Automake::Rule> is a rule name associated to possibly
+many conditional definitions.  These definitions are instances
+of C<Automake::RuleDef>.
+
+Therefore obtaining the value of a rule under a given
+condition involves two lookups.  One to look up the rule,
+and one to look up the conditional definition:
+
+  my $rule = rule $name;
+  if ($rule)
+    {
+      my $def = $rule->def ($cond);
+      if ($def)
+        {
+          return $def->location;
+        }
+      ...
+    }
+  ...
+
+when it is known that the rule and the definition
+being looked up exist, the above can be simplified to
+
+  return rule ($name)->def ($cond)->location; # do not write this.
+
+but is better written
+
+  return rrule ($name)->rrule ($cond)->location;
+
+or even
+
+  return rruledef ($name, $cond)->location;
+
+The I<r> variants of the C<rule>, C<def>, and C<ruledef> methods add
+an extra test to ensure that the lookup succeeded, and will diagnose
+failures as internal errors (with a message which is much more
+informative than Perl's warning about calling a method on a
+non-object).
+
+=head2 Global variables
+
+=over 4
+
+=cut
+
+my $_SUFFIX_RULE_PATTERN =
+  '^(\.[a-zA-Z0-9_(){}$+@]+)(\.[a-zA-Z0-9_(){}$+@]+)' . "\$";
+
+# Suffixes found during a run.
+use vars '@_suffixes';
+
+# Same as $suffix_rules (declared below), but records only the
+# default rules supplied by the languages Automake supports.
+use vars '$_suffix_rules_default';
+
+=item C<%dependencies>
+
+Holds the dependencies of targets which dependencies are factored.
+Typically, C<.PHONY> will appear in plenty of F<*.am> files, but must
+be output once.  Arguably all pure dependencies could be subject to
+this factorization, but it is not unpleasant to have paragraphs in
+Makefile: keeping related stuff altogether.
+
+=cut
+
+use vars '%dependencies';
+
+=item <%actions>
+
+Holds the factored actions.  Tied to C<%dependencies>, i.e., filled
+only when keys exists in C<%dependencies>.
+
+=cut
+
+use vars '%actions';
+
+=item <$suffix_rules>
+
+This maps the source extension for all suffix rule seen to
+a C<\hash> whose keys are the possible output extensions.
+
+Note that this is transitively closed by construction:
+if we have
+      exists $suffix_rules{$ext1}{$ext2}
+   && exists $suffix_rules{$ext2}{$ext3}
+then we also have
+      exists $suffix_rules{$ext1}{$ext3}
+
+So it's easy to check whether C<.foo> can be transformed to
+C<.$(OBJEXT)> by checking whether
+C<$suffix_rules{'.foo'}{'.$(OBJEXT)'}> exists.  This will work even if
+transforming C<.foo> to C<.$(OBJEXT)> involves a chain of several
+suffix rules.
+
+The value of C<$suffix_rules{$ext1}{$ext2}> is the a pair
+C<[ $next_sfx, $dist ]> where C<$next_sfx> is target suffix
+for the next rule to use to reach C<$ext2>, and C<$dist> the
+distance to C<$ext2'>.
+
+The content of this variable should be updated via the
+C<register_suffix_rule> function.
+
+=cut
+
+use vars '$suffix_rules';
+
+=item C<$KNOWN_EXTENSIONS_PATTERN>
+
+Pattern that matches all know input extensions (i.e. extensions used
+by the languages supported by Automake).  Using this pattern (instead
+of `\..*$') to match extensions allows Automake to support dot-less
+extensions.
+
+New extension should be registered with C<accept_extensions>.
+
+=cut
+
+use vars qw ($KNOWN_EXTENSIONS_PATTERN @_known_extensions_list);
+$KNOWN_EXTENSIONS_PATTERN = "";
+@_known_extensions_list = ();
+
+=back
+
+=head2 Error reporting functions
+
+In these functions, C<$rule> can be either a rule name, or
+an instance of C<Automake::Rule>.
+
+=over 4
+
+=item C<err_rule ($rule, $message, [%options])>
+
+Uncategorized errors about rules.
+
+=cut
+
+sub err_rule ($$;%)
+{
+  msg_rule ('error', @_);
+}
+
+=item C<err_cond_rule ($cond, $rule, $message, [%options])>
+
+Uncategorized errors about conditional rules.
+
+=cut
+
+sub err_cond_rule ($$$;%)
+{
+  msg_cond_rule ('error', @_);
+}
+
+=item C<msg_cond_rule ($channel, $cond, $rule, $message, [%options])>
+
+Messages about conditional rules.
+
+=cut
+
+sub msg_cond_rule ($$$$;%)
+{
+  my ($channel, $cond, $rule, $msg, %opts) = @_;
+  my $r = ref ($rule) ? $rule : rrule ($rule);
+  msg $channel, $r->rdef ($cond)->location, $msg, %opts;
+}
+
+=item C<msg_rule ($channel, $targetname, $message, [%options])>
+
+Messages about rules.
+
+=cute
+
+sub msg_rule ($$$;%)
+{
+  my ($channel, $rule, $msg, %opts) = @_;
+  my $r = ref ($rule) ? $rule : rrule ($rule);
+  # Don't know which condition is concerned.  Pick any.
+  my $cond = $r->conditions->one_cond;
+  msg_cond_rule ($channel, $cond, $r, $msg, %opts);
+}
+
+
+=item C<$bool = reject_rule ($rule, $error_msg)>
+
+Bail out with C<$error_msg> if a rule with name C<$rule> has been
+defined.
+
+Return true iff C<$rule> is defined.
+
+=cut
+
+sub reject_rule ($$)
+{
+  my ($rule, $msg) = @_;
+  if (rule ($rule))
+    {
+      err_rule $rule, $msg;
+      return 1;
+    }
+  return 0;
+}
+
+=back
+
+=head2 Administrative functions
+
+=over 4
+
+=item C<accept_extensions (@exts)>
+
+Update C<$KNOWN_EXTENSIONS_PATTERN> to recognize the extensions
+listed C<@exts>.  Extensions should contain a dot if needed.
+
+=cut
+
+sub accept_extensions (@)
+{
+    push @_known_extensions_list, @_;
+    $KNOWN_EXTENSIONS_PATTERN =
+       '(?:' . join ('|', map (quotemeta, @_known_extensions_list)) . ')';
+}
+
+=item C<rules>
+
+Returns the list of all L<Automake::Rule> instances.  (I.e., all
+rules defined so far.)
+
+=cut
+
+use vars '%_rule_dict';
+sub rules ()
+{
+  return values %_rule_dict;
+}
+
+
+=item C<Automake::Rule::reset>
+
+The I<forget all> function.  Clears all know rules and reset some
+other internal data.
+
+=cut
+
+sub reset()
+{
+  %_rule_dict = ();
+  @_suffixes = ();
+  # The first time we initialize the variables,
+  # we save the value of $suffix_rules.
+  if (defined $_suffix_rules_default)
+    {
+      $suffix_rules = $_suffix_rules_default;
+    }
+  else
+    {
+      $_suffix_rules_default = $suffix_rules;
+    }
+
+  %dependencies =
+    (
+     # Texinfoing.
+     'dvi'      => [],
+     'dvi-am'   => [],
+     'pdf'      => [],
+     'pdf-am'   => [],
+     'ps'       => [],
+     'ps-am'    => [],
+     'info'     => [],
+     'info-am'  => [],
+     'html'     => [],
+     'html-am'  => [],
+
+     # Installing/uninstalling.
+     'install-data-am'      => [],
+     'install-exec-am'      => [],
+     'uninstall-am'         => [],
+
+     'install-man'         => [],
+     'uninstall-man'       => [],
+
+     'install-info'         => [],
+     'install-info-am'      => [],
+     'uninstall-info'       => [],
+
+     'installcheck-am'      => [],
+
+     # Cleaning.
+     'clean-am'             => [],
+     'mostlyclean-am'       => [],
+     'maintainer-clean-am'  => [],
+     'distclean-am'         => [],
+     'clean'                => [],
+     'mostlyclean'          => [],
+     'maintainer-clean'     => [],
+     'distclean'            => [],
+
+     # Tarballing.
+     'dist-all'             => [],
+
+     # Phoning.
+     '.PHONY'               => [],
+     );
+  %actions = ();
+}
+
+=item C<register_suffix_rule ($where, $src, $dest)>
+
+Register a suffix rules defined on C<$where> that transform
+files ending in C<$src> into files ending in C<$dest>.
+
+This upgrades the C<$suffix_rules> variables.
+
+=cut
+
+sub register_suffix_rule ($$$)
+{
+  my ($where, $src, $dest) = @_;
+
+  verb "Sources ending in $src become $dest";
+  push @_suffixes, $src, $dest;
+
+  # When tranforming sources to objects, Automake uses the
+  # %suffix_rules to move from each source extension to
+  # `.$(OBJEXT)', not to `.o' or `.obj'.  However some people
+  # define suffix rules for `.o' or `.obj', so internally we will
+  # consider these extensions equivalent to `.$(OBJEXT)'.  We
+  # CANNOT rewrite the target (i.e., automagically replace `.o'
+  # and `.obj' by `.$(OBJEXT)' in the output), or warn the user
+  # that (s)he'd better use `.$(OBJEXT)', because Automake itself
+  # output suffix rules for `.o' or `.obj'...
+  $dest = '.$(OBJEXT)' if ($dest eq '.o' || $dest eq '.obj');
+
+  # Reading the comments near the declaration of $suffix_rules might
+  # help to understand the update of $suffix_rules that follows...
+
+  # Register $dest as a possible destination from $src.
+  # We might have the create the \hash.
+  if (exists $suffix_rules->{$src})
+    {
+      $suffix_rules->{$src}{$dest} = [ $dest, 1 ];
+    }
+  else
+    {
+      $suffix_rules->{$src} = { $dest => [ $dest, 1 ] };
+    }
+
+  # If we know how to transform $dest in something else, then
+  # we know how to transform $src in that "something else".
+  if (exists $suffix_rules->{$dest})
+    {
+      for my $dest2 (keys %{$suffix_rules->{$dest}})
+       {
+         my $dist = $suffix_rules->{$dest}{$dest2}[1] + 1;
+         # Overwrite an existing $src->$dest2 path only if
+         # the path via $dest which is shorter.
+         if (! exists $suffix_rules->{$src}{$dest2}
+             || $suffix_rules->{$src}{$dest2}[1] > $dist)
+           {
+             $suffix_rules->{$src}{$dest2} = [ $dest, $dist ];
+           }
+       }
+    }
+
+  # Similarly, any extension that can be derived into $src
+  # can be derived into the same extenstions as $src can.
+  my @dest2 = keys %{$suffix_rules->{$src}};
+  for my $src2 (keys %$suffix_rules)
+    {
+      if (exists $suffix_rules->{$src2}{$src})
+       {
+         for my $dest2 (@dest2)
+           {
+             my $dist = $suffix_rules->{$src}{$dest2} + 1;
+             # Overwrite an existing $src2->$dest2 path only if
+             # the path via $src is shorter.
+             if (! exists $suffix_rules->{$src2}{$dest2}
+                 || $suffix_rules->{$src2}{$dest2}[1] > $dist)
+               {
+                 $suffix_rules->{$src2}{$dest2} = [ $src, $dist ];
+               }
+           }
+       }
+    }
+}
+
+=item C<$count = suffix_rules_count>
+
+Return the number of suffix rules added while processing the current
+F<Makefile> (excluding predefined suffix rules).
+
+=cut
+
+sub suffix_rules_count ()
+{
+  return (scalar keys %$suffix_rules) - (scalar keys %$_suffix_rules_default);
+}
+
+=item C<@list = suffixes>
+
+Return the list of known suffixes.
+
+=cut
+
+sub suffixes ()
+{
+  return @_suffixes;
+}
+
+=item C<rule ($rulename)>
+
+Return the C<Automake::Rule> object for the rule
+named C<$rulename> if defined.   Return 0 otherwise.
+
+=cut
+
+sub rule ($)
+{
+  my ($name) = @_;
+  # Strip $(EXEEXT) from $name, so we can diagnose
+  # a clash if `ctags$(EXEEXT):' is redefined after `ctags:'.
+  $name =~ s,\$\(EXEEXT\)$,,;
+  return $_rule_dict{$name} if exists $_rule_dict{$name};
+  return 0;
+}
+
+=item C<rule ($rulename, $cond>
+
+Return the C<Automake::RuleDef> object for the rule named
+C<$rulename> if defined in condition C<$cond>.  Return false
+if the condition or the rule does not exist.
+
+=cut
+
+sub ruledef ($$)
+{
+  my ($name, $cond) = @_;
+  my $rule = rule $name;
+  return $rule && $rule->def ($cond);
+}
+
+=item C<rrule ($rulename)
+
+Return the C<Automake::Rule> object for the variable named
+C<$rulename>.  Abort with an internal error if the variable was not
+defined.
+
+The I<r> in front of C<var> stands for I<required>.  One
+should call C<rvar> to assert the rule's existence.
+
+=cut
+
+sub rrule ($)
+{
+  my ($name) = @_;
+  my $r = rule $name;
+  prog_error ("undefined rule $name\n" . &rules_dump)
+    unless $r;
+  return $r;
+}
+
+=item C<rruledef ($varname, $cond)>
+
+Return the C<Automake::RuleDef> object for the rule named
+C<$rulename> if defined in condition C<$cond>.  Abort with an internal
+error if the condition or the rule does not exist.
+
+=cut
+
+sub rruledef ($$)
+{
+  my ($name, $cond) = @_;
+  return rrule ($name)->rdef ($cond);
+}
+
+# Create the variable if it does not exist.
+# This is used only by other functions in this package.
+sub _crule ($)
+{
+  my ($name) = @_;
+  my $r = rule $name;
+  return $r if $r;
+  return _new Automake::Rule $name;
+}
+
+sub _new ($$)
+{
+  my ($class, $name) = @_;
+
+  # Strip $(EXEEXT) from $name, so we can diagnose
+  # a clash if `ctags$(EXEEXT):' is redefined after `ctags:'.
+  (my $keyname = $name) =~ s,\$\(EXEEXT\)$,,;
+
+  my $self = Automake::Item::new ($class, $name);
+  $_rule_dict{$keyname} = $self;
+  return $self;
+}
+
+
+=itcem C<@conds = define ($rulename, $source, $owner, $cond, $where)>
+
+Define a new rule.  C<$rulename> is the list of targets.  C<$source>
+is the filename the rule comes from.  C<$owner> is the owner of the
+rule (C<RULE_AUTOMAKE> or C<RULE_USER>).  C<$cond> is the
+C<Automake::Condition> under which the rule is defined.  C<$where> is
+the C<Automake::Location> where the rule is defined.
+
+Returns a (possibly empty) list of C<Automake::Condition>s where the
+rule's definition should be output.
+
+=cut
+
+sub define ($$$$$)
+{
+  my ($target, $source, $owner, $cond, $where) = @_;
+
+  prog_error "$where is not a reference"
+    unless ref $where;
+  prog_error "$cond is not a reference"
+    unless ref $cond;
+
+  # Don't even think about defining a rule in condition FALSE.
+  return () if $cond == FALSE;
+
+  # For now `foo:' will override `foo$(EXEEXT):'.  This is temporary,
+  # though, so we emit a warning.
+  (my $noexe = $target) =~ s,\$\(EXEEXT\)$,,;
+  my $noexerule = rule $noexe;
+  my $tdef = $noexerule ? $noexerule->def ($cond) : undef;
+
+  if ($noexe ne $target
+      && $tdef
+      && $noexerule->name ne $target)
+    {
+      print "1. $noexe\n";
+      print "2. $target\n";
+      print "3. " . $noexerule->name . "\n";
+      # The no-exeext option enables this feature.
+      if (! option 'no-exeext')
+       {
+         msg ('obsolete', $tdef->location,
+              "deprecated feature: target `$noexe' overrides "
+              . "`$noexe\$(EXEEXT)'\n"
+              . "change your target to read `$noexe\$(EXEEXT)'");
+         msg ('obsolete', $where, "target `$target' was defined here");
+       }
+      # Don't `return ()' now, as this might hide target clashes
+      # detected below.
+    }
+
+
+  # A GNU make-style pattern rule has a single "%" in the target name.
+  msg ('portability', $where,
+       "`%'-style pattern rules are a GNU make extension")
+    if $target =~ /^[^%]*%[^%]*$/;
+
+  # Diagnose target redefinitions.
+  if ($tdef)
+    {
+      my $oldowner  = $tdef->owner;
+      # Ok, it's the name target, but the name maybe different because
+      # `foo$(EXEEXT)' and `foo' have the same key in our table.
+      my $oldname = $tdef->name;
+
+      # Don't mention true conditions in diagnostics.
+      my $condmsg =
+       $cond == TRUE ? '' : " in condition `" . $cond->human . "'";
+
+      if ($owner == RULE_USER)
+       {
+         if ($oldowner == RULE_USER)
+           {
+             # Ignore `%'-style pattern rules.  We'd need the
+             # dependencies to detect duplicates, and they are
+             # already diagnosed as unportable by -Wportability.
+             if ($target !~ /^[^%]*%[^%]*$/)
+               {
+                 ## FIXME: Presently we can't diagnose duplcate user rules
+                 ## because we doesn't distinguish rules with commands
+                 ## from rules that only add dependencies.  E.g.,
+                 ##   .PHONY: foo
+                 ##   .PHONY: bar
+                 ## is legitimate. (This is phony.test.)
+
+                 # msg ('syntax', $where,
+                 #      "redefinition of `$target'$condmsg...", partial => 1);
+                 # msg_cond_rule ('syntax', $cond, $target,
+                 #                "... `$target' previously defined here");
+               }
+             # Return so we don't redefine the rule in our tables,
+             # don't check for ambiguous condition, etc.  The rule
+             # will be output anyway beauce &read_am_file ignore the
+             # return code.
+             return ();
+           }
+         else
+           {
+             # Since we parse the user Makefile.am before reading
+             # the Automake fragments, this condition should never happen.
+             prog_error ("user target `$target'$condmsg seen after Automake's"
+                         . " definition\nfrom " . $tdef->source);
+           }
+       }
+      else # $owner == RULE_AUTOMAKE
+       {
+         if ($oldowner == RULE_USER)
+           {
+             # -am targets listed in %dependencies support a -local
+             # variant.  If the user tries to override TARGET or
+             # TARGET-am for which there exists a -local variant,
+             # just tell the user to use it.
+             my $hint = 0;
+             my $noam = $target;
+             $noam =~ s/-am$//;
+             if (exists $dependencies{"$noam-am"})
+               {
+                 $hint = "consider using $target-local instead of $target";
+               }
+
+             msg_cond_rule ('override', $cond, $target,
+                            "user target `$target' defined here"
+                            . "$condmsg...", partial => 1);
+             msg ('override', $where,
+                  "... overrides Automake target `$oldname' defined here",
+                  partial => $hint);
+             msg_cond_rule ('override', $cond, $target, $hint)
+               if $hint;
+
+             # Don't overwrite the user definition of TARGET.
+             return ();
+           }
+         else # $oldowner == RULE_AUTOMAKE
+           {
+             # Automake should ignore redefinitions of its own
+             # rules if they came from the same file.  This makes
+             # it easier to process a Makefile fragment several times.
+             # Hower it's an error if the target is defined in many
+             # files.  E.g., the user might be using bin_PROGRAMS = ctags
+             # which clashes with our `ctags' rule.
+             # (It would be more accurate if we had a way to compare
+             # the *content* of both rules.  Then $targets_source would
+             # be useless.)
+             my $oldsource = $tdef->source;
+             return () if $source eq $oldsource && $target eq $oldname;
+
+             msg ('syntax', $where, "redefinition of `$target'$condmsg...",
+                  partial => 1);
+             msg_cond_rule ('syntax', $cond, $target,
+                            "... `$oldname' previously defined here");
+             return ();
+           }
+       }
+      # Never reached.
+      prog_error ("Unreachable place reached.");
+    }
+
+  # Conditions for which the rule should be defined.
+  my @conds = $cond;
+
+  # Check ambiguous conditional definitions.
+  my $rule = _crule $target;
+  my ($message, $ambig_cond) = $rule->conditions->ambiguous_p ($target, $cond);
+  if ($message)                        # We have an ambiguty.
+    {
+      if ($owner == RULE_USER)
+       {
+         # For user rules, just diagnose the ambiguity.
+         msg 'syntax', $where, "$message ...", partial => 1;
+         msg_cond_rule ('syntax', $ambig_cond, $target,
+                        "... `$target' previously defined here");
+         return ();
+       }
+      else
+       {
+         # FIXME: for Automake rules, we can't diagnose ambiguities yet.
+         # The point is that Automake doesn't propagate conditions
+         # everywhere.  For instance &handle_PROGRAMS doesn't care if
+         # bin_PROGRAMS was defined conditionally or not.
+         # On the following input
+         #   if COND1
+         #   foo:
+         #           ...
+         #   else
+         #   bin_PROGRAMS = foo
+         #   endif
+         # &handle_PROGRAMS will attempt to define a `foo:' rule
+         # in condition TRUE (which conflicts with COND1).  Fixing
+         # this in &handle_PROGRAMS and siblings seems hard: you'd
+         # have to explain &file_contents what to do with a
+         # condition.  So for now we do our best *here*.  If `foo:'
+         # was already defined in condition COND1 and we want to define
+         # it in condition TRUE, then define it only in condition !COND1.
+         # (See cond14.test and cond15.test for some test cases.)
+         @conds = ();
+         for my $undefined_cond ($rule->conditions->invert->conds)
+           {
+             push @conds, $cond->merge ($undefined_cond);
+           }
+         # No conditions left to define the rule.
+         # Warn, because our workaround is meaningless in this case.
+         if (scalar @conds == 0)
+           {
+             msg 'syntax', $where, "$message ...", partial => 1;
+             msg_cond_rule ('syntax', $ambig_cond, $target,
+                            "... `$target' previously defined here");
+             return ();
+           }
+       }
+    }
+
+  # Finally define this rule.
+  for my $c (@conds)
+    {
+      my $def = new Automake::RuleDef ($target, '', $where->clone,
+                                      $owner, $source);
+      $rule->set ($c, $def);
+    }
+
+  # We honor inference rules with multiple targets because many
+  # make support this and people use it.  However this is disallowed
+  # by POSIX.  We'll print a warning later.
+  my $target_count = 0;
+  my $inference_rule_count = 0;
+
+  for my $t (split (' ', $target))
+    {
+      ++$target_count;
+      # Check the rule for being a suffix rule. If so, store in a hash.
+      # Either it's a rule for two known extensions...
+      if ($t =~ /^($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).
+
+
+         || ($t =~ /$_SUFFIX_RULE_PATTERN/o && accept_extensions($1)))
+       {
+         ++$inference_rule_count;
+         register_suffix_rule ($where, $1, $2);
+       }
+    }
+
+  # POSIX allow multiple targets befor the colon, but disallow
+  # definitions of multiple Inference rules.  It's also
+  # disallowed to mix plain targets with inference rules.
+  msg ('portability', $where,
+       "Inference rules can have only one target before the colon (POSIX).")
+    if $inference_rule_count > 0 && $target_count > 1;
+
+  return @conds;
+}
+
+=item C<depend ($target, @deps)>
+
+Adds C<@deps> to the dependencies of target C<$target>.  This should
+be used only with factored targets (those appearing in
+C<%dependendees>).
+
+=cut
+
+sub depend ($@)
+{
+  my ($category, @dependendees) = @_;
+  push (@{$dependencies{$category}}, @dependendees);
+}
+
+=back
+
+=head1 SEE ALSO
+
+L<Automake::RuleDef>, L<Automake::Condition>,
+L<Automake::DisjConditions>, L<Automake::Location>.
+
+=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:
diff --git a/lib/Automake/RuleDef.pm b/lib/Automake/RuleDef.pm
new file mode 100644 (file)
index 0000000..d25c8e7
--- /dev/null
@@ -0,0 +1,102 @@
+# Copyright (C) 2003  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::RuleDef;
+use strict;
+use Carp;
+use Automake::ChannelDefs;
+use Automake::ItemDef;
+
+require Exporter;
+use vars '@ISA', '@EXPORT';
+@ISA = qw/Automake::ItemDef Exporter/;
+@EXPORT = qw (&RULE_AUTOMAKE &RULE_USER);
+
+=head1 NAME
+
+Automake::RuleDef - a class for rule definitions
+
+=head1 SYNOPSIS
+
+  use Automake::RuleDef;
+  use Automake::Location;
+
+=head1 DESCRIPTION
+
+This class gather data related to one Makefile-rule definition.
+
+=head2 Constants
+
+=over 4
+
+=item C<RULE_AUTOMAKE>, C<RULE_USER>
+
+Possible owners for rules.
+
+=cut
+
+use constant RULE_AUTOMAKE => 0; # Rule defined by Automake.
+use constant RULE_USER => 1;     # Rule defined in the user's Makefile.am.
+
+sub new ($$$$$)
+{
+  my ($class, $name, $comment, $location, $owner, $source) = @_;
+
+  my $self = Automake::ItemDef::new ($class, $comment, $location, $owner);
+  $self->{'source'} = $source;
+  $self->{'name'} = $name;
+  return $self;
+}
+
+sub source ($)
+{
+  my ($self) = @_;
+  return $self->{'source'};
+}
+
+sub name ($)
+{
+  my ($self) = @_;
+  return $self->{'name'};
+}
+
+=back
+
+=head1 SEE ALSO
+
+L<Automake::Rule>, L<Automake::ItemDef>.
+
+=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 7fb53e6..dde29c4 100644 (file)
@@ -19,10 +19,11 @@ package Automake::VarDef;
 use strict;
 use Carp;
 use Automake::ChannelDefs;
+use Automake::ItemDef;
 
 require Exporter;
 use vars '@ISA', '@EXPORT';
-@ISA = qw/Exporter/;
+@ISA = qw/Automake::ItemDef Exporter/;
 @EXPORT = qw (&VAR_AUTOMAKE &VAR_CONFIGURE &VAR_MAKEFILE
              &VAR_ASIS &VAR_PRETTY &VAR_SILENT &VAR_SORTED);
 
@@ -112,9 +113,12 @@ use constant VAR_SORTED => 3;      # Sorted and pretty-printed.
 
 =head2 Methods
 
+C<VarDef> defines the following methods in addition to those inherited
+from L<Automake::ItemDef>.
+
 =over 4
 
-=item C<my $def = Automake::new ($varname, $value, $comment, $location, $type, $owner, $pretty)>
+=item C<my $def = new Automake::VarDef ($varname, $value, $comment, $location, $type, $owner, $pretty)>
 
 Create a new Makefile-variable definition.  C<$varname> is the name of
 the variable being defined and C<$value> its value.
@@ -150,17 +154,11 @@ sub new ($$$$$$$$)
       error $location, "$var must be set with `=' before using `+='";
     }
 
-  my $self = {
-    value => $value,
-    comment => $comment,
-    location => $location,
-    type => $type,
-    owner => $owner,
-    pretty => $pretty,
-    seen => 0,
-  };
-  bless $self, $class;
-
+  my $self = Automake::ItemDef::new ($class, $comment, $location, $owner);
+  $self->{'value'} = $value;
+  $self->{'type'} = $type;
+  $self->{'pretty'} = $pretty;
+  $self->{'seen'} = 0;
   return $self;
 }
 
@@ -192,14 +190,8 @@ sub append ($$$)
 
 =item C<$def-E<gt>value>
 
-=item C<$def-E<gt>comment>
-
-=item C<$def-E<gt>location>
-
 =item C<$def-E<gt>type>
 
-=item C<$def-E<gt>owner>
-
 =item C<$def-E<gt>pretty>
 
 Accessors to the various constituents of a C<VarDef>.  See the
@@ -213,30 +205,12 @@ sub value ($)
   return $self->{'value'};
 }
 
-sub comment ($)
-{
-  my ($self) = @_;
-  return $self->{'comment'};
-}
-
-sub location ($)
-{
-  my ($self) = @_;
-  return $self->{'location'};
-}
-
 sub type ($)
 {
   my ($self) = @_;
   return $self->{'type'};
 }
 
-sub owner ($)
-{
-  my ($self) = @_;
-  return $self->{'owner'};
-}
-
 sub pretty ($)
 {
   my ($self) = @_;
@@ -330,7 +304,7 @@ sub dump ($)
 
 =head1 SEE ALSO
 
-L<Automake::Variable>.
+L<Automake::Variable>, L<Automake::ItemDef>.
 
 =cut
 
index 4cce9fb..cd716c7 100644 (file)
 package Automake::Variable;
 use strict;
 use Carp;
+
 use Automake::Channels;
 use Automake::ChannelDefs;
 use Automake::Configure_ac;
+use Automake::Item;
 use Automake::VarDef;
 use Automake::Condition qw (TRUE FALSE);
 use Automake::DisjConditions;
@@ -29,7 +31,7 @@ use Automake::Wrap 'makefile_wrap';
 
 require Exporter;
 use vars '@ISA', '@EXPORT', '@EXPORT_OK';
-@ISA = qw/Exporter/;
+@ISA = qw/Automake::Item Exporter/;
 @EXPORT = qw (err_var msg_var msg_cond_var reject_var
              var rvar vardef rvardef
              variables
@@ -122,7 +124,7 @@ or even
 
 The I<r> variants of the C<var>, C<def>, and C<vardef> methods add an
 extra test to ensure that the lookup succeeded, and will diagnose
-failures as internal errors (which a message which is much more
+failures as internal errors (with a message which is much more
 informative than Perl's warning about calling a method on a
 non-object).
 
@@ -240,16 +242,15 @@ sub msg_var ($$$;%)
   msg_cond_var $channel, $cond, $v, $msg, %opts;
 }
 
-=item C<reject_var ($varname, $error_msg)>
+=item C<$bool = reject_var ($varname, $error_msg)>
 
-Bail out with C<$ERROR_MSG> if a variable with name C<$VARNAME> has
+Bail out with C<$error_msg> if a variable with name C<$varname> has
 been defined.
 
+Return true iff C<$varname> is defined.
+
 =cut
 
-# $BOOL
-# reject_var ($VARNAME, $ERROR_MSG)
-# -----------------------------
 sub reject_var ($$)
 {
   my ($var, $msg) = @_;
@@ -333,7 +334,7 @@ sub var ($)
 =item C<vardef ($varname, $cond)>
 
 Return the C<Automake::VarDef> object for the variable named
-C<$varname> if defined in condition C<$cond>.  Return the empty list
+C<$varname> if defined in condition C<$cond>.  Return false
 if the condition or the variable does not exist.
 
 =cut
@@ -379,7 +380,7 @@ sub rvar ($)
 
 Return the C<Automake::VarDef> object for the variable named
 C<$varname> if defined in condition C<$cond>.  Abort with an internal
-error if the variable or the variable does not exist.
+error if the condition or the variable does not exist.
 
 =cut
 
@@ -393,7 +394,10 @@ sub rvardef ($$)
 
 =head2 Methods
 
-Here are the methods of the C<Automake::Variable> instances.
+C<Automake::Variable> is a subclass of C<Automake::Item>.  See
+that package for inherited methods.
+
+Here are the methods specific to the C<Automake::Variable> instances.
 Use the C<define> function, described latter, to create such objects.
 
 =over 4
@@ -406,92 +410,12 @@ Use the C<define> function, described latter, to create such objects.
 sub _new ($$)
 {
   my ($class, $name) = @_;
-  my $self = {
-    name => $name,
-    defs => {},
-    conds => {},
-    scanned => 0,
-  };
-  bless $self, $class;
+  my $self = Automake::Item::new ($class, $name);
+  $self->{'scanned'} = 0;
   $_variable_dict{$name} = $self;
   return $self;
 }
 
-=item C<$var-E<gt>name>
-
-Return the name of C<$var>.
-
-=cut
-
-sub name ($)
-{
-  my ($self) = @_;
-  return $self->{'name'};
-}
-
-=item C<$var-E<gt>def ($cond)>
-
-Return the C<Automake::VarDef> definition for this variable in
-condition C<$cond>, if it exists.  Return 0 otherwise.
-
-=cut
-
-sub def ($$)
-{
-  my ($self, $cond) = @_;
-  return $self->{'defs'}{$cond} if exists $self->{'defs'}{$cond};
-  return 0;
-}
-
-=item C<$var-E<gt>rdef ($cond)>
-
-Return the C<Automake::VarDef> definition for this variable in
-condition C<$cond>.  Abort with an internal error if the variable was
-not defined under this condition.
-
-The I<r> in front of C<def> stands for I<required>.  One
-should call C<rdef> to assert the conditional definition's existence.
-
-=cut
-
-sub rdef ($$)
-{
-  my ($self, $cond) = @_;
-  my $d = $self->def ($cond);
-  prog_error ("undefined condition `" . $cond->human . "' for `"
-             . $self->name . "'\n" . $self->dump)
-    unless $d;
-  return $d;
-}
-
-# Add a new VarDef to an existing Variable.  This is a private
-# function.  Our public interface is the `define' function.
-sub _set ($$$)
-{
-  my ($self, $cond, $def) = @_;
-  $self->{'defs'}{$cond} = $def;
-  $self->{'conds'}{$cond} = $cond;
-}
-
-=item C<$var-E<gt>conditions>
-
-Return an L<Automake::DisjConditions> describing the conditions that
-that a variable is defined with, without recursing through the
-conditions of any subvariables.
-
-These are all the conditions for which is would be safe to call
-C<rdef>.
-
-=cut
-
-sub conditions ($)
-{
-  my ($self) = @_;
-  prog_error ("self is not a reference")
-    unless ref $self;
-  return new Automake::DisjConditions (values %{$self->{'conds'}});
-}
-
 # _check_ambiguous_condition ($SELF, $COND, $WHERE)
 # -------------------------------------------------
 # Check for an ambiguous conditional.  This is called when a variable
@@ -514,63 +438,6 @@ sub _check_ambiguous_condition ($$$)
     }
 }
 
-=item C<@missing_conds = $var-E<gt>not_always_defined_in_cond ($cond)>
-
-Check whether C<$var> is always defined for condition C<$cond>.
-Return a list of conditions where the definition is missing.
-
-For instance, given
-
-  if COND1
-    if COND2
-      A = foo
-      D = d1
-    else
-      A = bar
-      D = d2
-    endif
-  else
-    D = d3
-  endif
-  if COND3
-    A = baz
-    B = mumble
-  endif
-  C = mumble
-
-we should have (we display result as conditional strings in this
-illustration, but we really return DisjConditions objects):
-
-  var ('A')->not_always_defined_in_cond ('COND1_TRUE COND2_TRUE')
-    => ()
-  var ('A')->not_always_defined_in_cond ('COND1_TRUE')
-    => ()
-  var ('A')->not_always_defined_in_cond ('TRUE')
-    => ("COND1_FALSE COND3_FALSE")
-  var ('B')->not_always_defined_in_cond ('COND1_TRUE')
-    => ("COND1_TRUE COND3_FALSE")
-  var ('C')->not_always_defined_in_cond ('COND1_TRUE')
-    => ()
-  var ('D')->not_always_defined_in_cond ('TRUE')
-    => ()
-  var ('Z')->not_always_defined_in_cond ('TRUE')
-    => ("TRUE")
-
-=cut
-
-sub not_always_defined_in_cond ($$)
-{
-  my ($self, $cond) = @_;
-
-  # Compute the subconditions where $var isn't defined.
-  return
-    $self->conditions
-      ->sub_conditions ($cond)
-       ->invert
-         ->simplify
-           ->multiply ($cond);
-}
-
 =item C<$bool = $var-E<gt>check_defined_unconditionally ([$parent, $parent_cond])>
 
 Warn if the variable is conditionally defined.  C<$parent> is the name
@@ -1114,7 +981,7 @@ sub define ($$$$$$$$)
          # line numbers with random bits of text.
          $def = new Automake::VarDef ($var, $value, $comment, $where->clone,
                                       $type, $owner, $pretty);
-         $self->_set ($cond, $def);
+         $self->set ($cond, $def);
          push @_var_order, $var;
 
          # No need to adjust the owner later as we have overridden
index dc5f948..8c098ea 100755 (executable)
@@ -65,12 +65,12 @@ library.am: change your target to read `libfoo.a$(EXEEXT)'
 Makefile.am:3:   while processing library `libfoo.a'
 program.am: target `libfoo.a$(EXEEXT)' was defined here
 Makefile.am:1:   while processing program `libfoo.a'
-program.am: redefinition of `libfoo.a'...
+program.am: redefinition of `libfoo.a$(EXEEXT)'...
 Makefile.am:1:   while processing program `libfoo.a'
 library.am: ... `libfoo.a' previously defined here
 Makefile.am:3:   while processing library `libfoo.a'
 tags.am: redefinition of `ctags'...
-program.am: ... `ctags' previously defined here
+program.am: ... `ctags$(EXEEXT)' previously defined here
 Makefile.am:6:   while processing program `ctags'
 EOF