# 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:
