automake-patches
[Top][All Lists]
Advanced

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

Automake::Rule, Automake::RuleDef, Automake::Item, Automake::ItemDef


From: Alexandre Duret-Lutz
Subject: Automake::Rule, Automake::RuleDef, Automake::Item, Automake::ItemDef
Date: Sat, 09 Aug 2003 15:13:12 +0200
User-agent: Gnus/5.1002 (Gnus v5.10.2) Emacs/21.3 (gnu/linux)

Here is a first attempt at creating a Rule class.  Please load
your comment cannons and fire at will...

Random notes:

 - This patch does not move all the stuff that could be moved to
   Rule.  I prefer to make small steps, and these are the
   minimal moves I had to do in order to get rid of the
   Tie::RefHash::Nestable uses.  (BTW it means Perl < 5.6 should
   be supported again, unless there are other quirks that were
   hidden by the Nestable issue.  I haven't checked but I plan
   to do so before 1.8.)

 - Unlike the Variable class, the Rule class does not output
   its definitions.  This should be done eventually, and would
   help fixing a number of issues in Automake (for instance see
   the couple of FIXME:s in file_contents_internal), but it's
   too much for me now.

 - In case it's not obvious from the patch and earlier mails,
   the hierarchies are
   
            Item             ItemDef
            /  \              /   \
         Rule  Variable  RuleDef VariableDef

   I've opted for "Rule" over "Target" (which is the term
   used presently) because I'd like this class to store 
   dependencies and actions in the future.  So that
     Rule = Target + Dependencies + Actions.

   I'm not proud of "Item", but this is all I could find in my
   limited vocabulary.

 - More sharing between Rule and Variable could be achieved 
   by introducing ItemDict/RuleDict/VarDict classes.  Introducing
   these classes would also make possible to have per-Makefile
   dictionaries, which probably is a good thing.

 - Looks like the msg_* functions from Rule and Variable deserve
   factorization.  Maybe they could be defined as method of Item?


2003-08-09  Alexandre Duret-Lutz  <address@hidden>

        * 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.

Index: automake.in
===================================================================
RCS file: /cvs/automake/automake/automake.in,v
retrieving revision 1.1497
diff -u -r1.1497 automake.in
--- automake.in 7 Aug 2003 21:20:11 -0000       1.1497
+++ automake.in 9 Aug 2003 12:37:58 -0000
@@ -135,9 +135,10 @@
 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 $RULE_PATTERN =
   "^($TARGET_PATTERN(?:(?:\\\\\n|\\s)+$TARGET_PATTERN)*) *:([^=].*|)\$";
 
-my $SUFFIX_RULE_PATTERN =
-    '^(\.[a-zA-Z0-9_()address@hidden)(\.[a-zA-Z0-9_()address@hidden)' . "\$";
 # 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 @@
 # 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_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 @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 @@
 # 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 @@
 
 ################################################################
 
-# 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 @@
     $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 @@
     @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 @@
 
     %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 @@
 
 # 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 @@
   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 @@
   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 @@
     # 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 @@
     {
       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 @@
     }
 
   # 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 @@
   # 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 @@
     $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 @@
                             ? (" \$(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 @@
       }
 
     # Install `all' hooks.
-    if (&target_defined ("all-local"))
+    if (rule "all-local")
     {
       push (@all, "all-local");
       &depend ('.PHONY', "all-local");
@@ -4036,7 +3833,7 @@
 # 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 @@
     {
       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 @@
     {
       # 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 @@
   # 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"} .=
            ("address@hidden(NORMAL_INSTALL)\n"
@@ -4205,7 +4002,8 @@
       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 @@
     {
       foreach my $dest (&{$lang->output_extensions} ($suffix))
        {
-         &register_suffix_rule ('internal', $suffix, $dest);
+         register_suffix_rule (INTERNAL, $suffix, $dest);
        }
     }
 }
@@ -5260,18 +5058,6 @@
 ## ------------------------ ##
 
 
-# @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 @@
 
 ################################################################
 
-## ---------------- ##
-## 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 @@
            # 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 @@
                  # 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 @@
                # 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");
            }
Index: lib/Automake/Item.pm
===================================================================
RCS file: lib/Automake/Item.pm
diff -N lib/Automake/Item.pm
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ lib/Automake/Item.pm        9 Aug 2003 12:38:00 -0000
@@ -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:
Index: lib/Automake/ItemDef.pm
===================================================================
RCS file: lib/Automake/ItemDef.pm
diff -N lib/Automake/ItemDef.pm
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ lib/Automake/ItemDef.pm     9 Aug 2003 12:38:00 -0000
@@ -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: lib/Automake/Makefile.am
===================================================================
RCS file: /cvs/automake/automake/lib/Automake/Makefile.am,v
retrieving revision 1.18
diff -u -r1.18 Makefile.am
--- lib/Automake/Makefile.am    7 Aug 2003 20:30:04 -0000       1.18
+++ lib/Automake/Makefile.am    9 Aug 2003 12:38:00 -0000
@@ -28,8 +28,12 @@
   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: lib/Automake/Makefile.in
===================================================================
RCS file: /cvs/automake/automake/lib/Automake/Makefile.in,v
retrieving revision 1.84
diff -u -r1.84 Makefile.in
--- lib/Automake/Makefile.in    7 Aug 2003 20:30:04 -0000       1.84
+++ lib/Automake/Makefile.in    9 Aug 2003 12:38:00 -0000
@@ -138,8 +138,12 @@
   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: lib/Automake/Rule.pm
===================================================================
RCS file: lib/Automake/Rule.pm
diff -N lib/Automake/Rule.pm
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ lib/Automake/Rule.pm        9 Aug 2003 12:38:00 -0000
@@ -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';
address@hidden = qw/Automake::Item Exporter/;
address@hidden = 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_()address@hidden)(\.[a-zA-Z0-9_()address@hidden)' . "\$";
+
+# 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 = "";
address@hidden = ();
+
+=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:
Index: lib/Automake/RuleDef.pm
===================================================================
RCS file: lib/Automake/RuleDef.pm
diff -N lib/Automake/RuleDef.pm
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ lib/Automake/RuleDef.pm     9 Aug 2003 12:38:00 -0000
@@ -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';
address@hidden = qw/Automake::ItemDef Exporter/;
address@hidden = 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: lib/Automake/VarDef.pm
===================================================================
RCS file: /cvs/automake/automake/lib/Automake/VarDef.pm,v
retrieving revision 1.2
diff -u -r1.2 VarDef.pm
--- lib/Automake/VarDef.pm      6 Jul 2003 19:27:29 -0000       1.2
+++ lib/Automake/VarDef.pm      9 Aug 2003 12:38:01 -0000
@@ -19,10 +19,11 @@
 use strict;
 use Carp;
 use Automake::ChannelDefs;
+use Automake::ItemDef;
 
 require Exporter;
 use vars '@ISA', '@EXPORT';
address@hidden = qw/Exporter/;
address@hidden = qw/Automake::ItemDef Exporter/;
 @EXPORT = qw (&VAR_AUTOMAKE &VAR_CONFIGURE &VAR_MAKEFILE
              &VAR_ASIS &VAR_PRETTY &VAR_SILENT &VAR_SORTED);
 
@@ -112,9 +113,12 @@
 
 =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 @@
       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 @@
 
 =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 @@
   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 @@
 
 =head1 SEE ALSO
 
-L<Automake::Variable>.
+L<Automake::Variable>, L<Automake::ItemDef>.
 
 =cut
 
Index: lib/Automake/Variable.pm
===================================================================
RCS file: /cvs/automake/automake/lib/Automake/Variable.pm,v
retrieving revision 1.15
diff -u -r1.15 Variable.pm
--- lib/Automake/Variable.pm    7 Aug 2003 21:20:12 -0000       1.15
+++ lib/Automake/Variable.pm    9 Aug 2003 12:38:02 -0000
@@ -18,9 +18,11 @@
 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 @@
 
 require Exporter;
 use vars '@ISA', '@EXPORT', '@EXPORT_OK';
address@hidden = qw/Exporter/;
address@hidden = qw/Automake::Item Exporter/;
 @EXPORT = qw (err_var msg_var msg_cond_var reject_var
              var rvar vardef rvardef
              variables
@@ -122,7 +124,7 @@
 
 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 @@
   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 @@
 =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 @@
 
 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 @@
 
 =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 @@
 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 @@
     }
 }
 
-=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
@@ -1119,7 +986,7 @@
          # 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: tests/location.test
===================================================================
RCS file: /cvs/automake/automake/tests/location.test,v
retrieving revision 1.5
diff -u -r1.5 location.test
--- tests/location.test 3 Jul 2003 18:58:50 -0000       1.5
+++ tests/location.test 9 Aug 2003 12:38:02 -0000
@@ -65,12 +65,12 @@
 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
 

-- 
Alexandre Duret-Lutz





reply via email to

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