# Part of the A-A-P recipe executive: Dependency rules

# Copyright (C) 2002 Stichting NLnet Labs
# Permission to copy and use this file is specified in the file COPYING.
# If this file is missing you can find it here: http://www.a-a-p.org/COPYING


# A Depend object contains:
#  targetlist   - dictlist of targets
#  build_attr   - dictlist of build attributes (right after the ":")
#  sourcelist   - dictlist of sources
#  rpstack      - RecPos stack for where the commands were defined
#  commands     - string of command lines
#  builddir     - directory where "commands" are to be executed
#  matchstr     - for a rule: the string that matched %
#
# Illustration:
#       targetlist : {build_attr} sourcelist
#               commands

import os
import os.path
import string

from Error import *
import Filetype
from Util import *
from Message import *

class Depend:
   def __init__(self, targetlist, build_attr, sourcelist,
                                    work, rpstack, commands, builddir = None):
       self.targetlist = targetlist
       self.build_attr = build_attr
       self.sourcelist = sourcelist
       self.rpstack = rpstack
       self.commands = commands
       if builddir is None:
           self.builddir = os.getcwd()
       else:
           self.builddir = builddir
       self.matchstr = ''

       # Add nodes for all sources and targets, with a pointer back to the
       # node.  Also carries over the attributes to the node.
       work.dictlist_nodes(self.targetlist)
       work.dictlist_nodes(self.sourcelist)

   def __str__(self):
       from Dictlist import dictlist2str, dictlistattr2str

       return (dictlist2str(self.targetlist)
                   + " : "
                   + dictlistattr2str(self.build_attr)
                   + dictlist2str(self.sourcelist)
                   + "\n" + self.commands)


def depend_auto(work, node):
   """Find the implied dependencies for "node".  They are returned in
      node.auto_depend.
      If "node" changed since last time, regenerate the dependencies."""
   # Return quickly when the automatic dependencies were already generated.
   if not node.auto_depend is None:
       return

   # Don't generate automatic dependencies when "autodepend" is "off".
   if ((work.globals.has_key("autodepend")
               and work.globals["autodepend"] == "off")
           or (node.attributes.has_key("autodepend")
               and node.attributes["autodepend"] == "off")):
       return

   # Get the file type.
   node_name = node.get_name()
   if node.attributes.has_key("filetype"):
       ftype = node.attributes["filetype"]
   else:
       ftype = Filetype.ft_detect(node_name)
       if not ftype:
           msg_depend(_('Unknown type of file, no dependency check for "%s"')
                                                          % node.short_name())
           return

   # Make the name for the recipe that contains the automatic dependencies for
   # this node: "node_directory/aap/node_basename.aap"
   recipe_name = os.path.join(os.path.dirname(node_name),
                    os.path.join("aap", os.path.basename(node_name))) + ".aap"
   recipe = work.get_node(recipe_name, 1)

   # Trigger the rule to produce a dependency recipe for this node.
   from DoBuild import build_autodepend
   if not build_autodepend(work, recipe, ftype, node):
       return
   if not os.path.exists(recipe.name):
       msg_warning(_('Dependency file was not created: "%s"') % recipe.name)
       return

   # Read the generated recipe file.
   node.auto_depend = read_auto_depend(recipe, node_name)


def read_auto_depend(recipe, skipname):
   """Read a generated recipe file from node "recipe".
      We only want the part after the ":", the item before it may be wrong
      (gcc generates foo.o: foo.c foo.h).
      Ignore "skipname".
      Don't read the recipe as a normal recipe, that would cause trouble with
      things we don't want to find in there. """
   try:
       file = open(recipe.name, "r")
   except:
       raise UserError, _('Cannot open "%s" for reading.') % recipe.name

   from RecPos import RecPos
   from ParsePos import ParsePos
   rpstack = [ RecPos(recipe.name) ]

   # create an object to contain the file position
   fp = ParsePos(rpstack, file = file)
   fp.nextline()           # read the first (and only) line
   if fp.line is None:
       raise UserError, _('Nothing to read from "%s".') % recipe.name
   i = string.find(fp.line, ":")
   if i < 0:
       raise UserError, _('No colon found in "%s".') % recipe.name

   auto_depend = None
   if i + 1 < fp.line_len:
       from Dictlist import string2dictlist
       auto_depend = string2dictlist(rpstack, fp.line[i + 1:])

       # Remove the node itself.
       for k in auto_depend:
           if k["name"] == skipname:
               auto_depend.remove(k)

   # Check for trailing text.
   fp.nextline()
   if not fp.line is None:
       msg_warning(_('Found trailing text in "%s"') % recipe.name)

   file.close()
   return auto_depend



# vim: set sw=4 sts=4 tw=79 fo+=l: