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