# Part of the A-A-P recipe executive: Dependency rules
# Copyright (C) 2002-2003 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
# buildrecdict - the recdict of the recipe where it was defined or None
# use_recdict - recdict to be used when executing build commands
# matchstr - for a rule: the string that matched %
# startup - non-zero for a dependency defined in a startup recipe
#
# Illustration:
# targetlist : {build_attr} sourcelist
# commands
import os
import os.path
import string
from Error import *
from Util import *
from Message import *
class Depend:
def __init__(self, targetlist, build_attr, sourcelist,
work, rpstack, commands, builddir = None, recdict = 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.buildrecdict = recdict
self.keep_current_scope = 0 # Current scope overrules scope of
# build commands; used for rules and
# actions.
self.use_recdict = None
self.matchstr = ''
self.startup = 0
self.in_use = 0
# 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 get_scope_names(self, work):
"""
Get the list of scope names to be used for build commands. First the
one specified as build attribute. A scope specified for a source
is appended.
"""
# Attribute on dependency itself.
xscope = self.build_attr.get("scope")
if xscope:
ret = [ xscope ]
else:
ret = []
for s in self.sourcelist:
if s.get("scope"):
# Attribute on source mentioned for dependency.
ret.append(s["scope"])
else:
node = work.find_node(s["name"])
if node and node.attributes.get("scope"):
# Attribute on source node.
ret.append(node.attributes["scope"])
return ret
def depend_auto(work, recdict, node, node_dict, level):
"""
Find the implied dependencies for "node".
"node_dict" contains attributes specified for the node specifically for the
current dependency. They overrule attributes from the node itself.
The dependencies are returned in node.autodep_dictlist.
If "node" changed since last time, regenerate the dependencies.
"""
# Previously we returned quickly when the automatic dependencies were
# already generated, because we cannot be sure they are still up-to-date.
# Changed attributes might matter.
# Don't generate automatic dependencies when "AUTODEPEND" is "off".
if (recdict["_no"].get("AUTODEPEND") == "off"
or (node.attributes.get("autodepend") == "off"
or node_dict.get("autodepend") == "off")):
node.autodep_dictlist = []
return
# Get the file type.
ftype = node_dict.get("filetype")
if not ftype:
ftype = node.get_ftype(recdict)
if not ftype:
msg_depend(recdict,
_('Unknown type of file, no dependency check for "%s"')
% node.short_name(), level)
node.autodep_dictlist = []
return
# A compressed or ".in" file is not checked.
if ftype == "ignore":
node.autodep_dictlist = []
return
# Trigger the rule to produce a dependency recipe for this node.
# This will also update node.autodep_dictlist when node.autodep_recursive
# is set.
from DoBuild import build_autodepend
recipe = build_autodepend(work, recdict, ftype, node, node_dict, level)
# return silently when no dependencies could be generated
if not recipe:
node.autodep_dictlist = []
return
if not os.path.exists(recipe.name):
msg_warning(recdict,
_('Dependency file was not created: "%s"') % recipe.name)
node.autodep_dictlist = []
return
# Read the generated recipe file when not done already.
if not node.autodep_recursive:
node.autodep_dictlist = read_auto_depend(recdict, recipe,
node.get_name())
def read_auto_depend(recdict, 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:
fd = open(recipe.name)
except:
msg_error(recdict, _('Cannot open "%s" for reading.') % recipe.name)
return []
from ParsePos import ParsePos
from RecPos import RecPos
rpstack = [ RecPos(recipe.name) ]
# create an object to contain the file position
fp = ParsePos(rpstack, file = fd)
fp.nextline() # read the first (and only) line
if fp.line is None:
msg_depend(recdict, _('Nothing to read from "%s".') % recipe.name)
return []
i = string.find(fp.line, ":")
if os.name != "posix" and i == 1: # DOS filename: "C:\dir\foo.c"
i = string.find(fp.line, ":", 2)
if i < 0:
msg_error(recdict, _('No colon found in "%s".') % recipe.name)
return []
autodep_dictlist = []
if i + 1 < fp.line_len:
# Need to convert names with backslash-space to quoted name.
nl = ''
i = i + 1
item = ''
had_bsl = 0
had_q = 0
line_len = len(fp.line)
while 1:
if i >= line_len or is_white(fp.line[i]):
if item:
# End of an item, append it to "nl" with or without quotes.
if nl:
nl = nl + ' '
if had_bsl == 1:
nl = nl + enquote(item)
else:
nl = nl + item
if i >= line_len:
break
had_bsl = 0
had_q = 0
item = ''
elif (fp.line[i] == '\\' and i + 1 < line_len
and is_white(fp.line[i + 1]) and not had_q):
i = i + 1
had_bsl = 1 # found a backslashed space or tab
item = item + fp.line[i]
elif fp.line[i] == '"' or fp.line[i] == "'":
had_q = 1 # don't recognize backslash after a quote
item = item + fp.line[i]
else:
item = item + fp.line[i]
i = i + 1
from Dictlist import str2dictlist
autodep_dictlist = str2dictlist(rpstack, nl)
# Make the path absolute (that's faster, SRCPATH won't be used).
# Remove the node itself.
for k in autodep_dictlist[:]:
k["name"] = os.path.abspath(k["name"])
if k["name"] == skipname:
autodep_dictlist.remove(k)
# Check for trailing text.
fp.nextline()
if not fp.line is None:
msg_note(recdict, _('Found trailing text in "%s"') % recipe.name)
fd.close()
return autodep_dictlist
# vim: set sw=4 et sts=4 tw=79 fo+=l:
|