# Part of the A-A-P recipe executive: A Rule object # 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 Rule object contains: # targetlist - list of target patterns # build_attr - build attributes # sourcelist - list of source patterns # rpstack - RecPos stack where the commands were defined # commands - string of command lines # builddir - directory where "commands" are to be executed # # A Rule is also used for a typerule. The only difference is that instead of # patterns file types are used. # # Illustration: # :rule targetlist : {build_attr} sourcelist # commands import string, os, os.path from Error import * def _trymatch(rpstack, name, name_short, patlist): """Check if "name" matches with a pattern in dictlist "patlist". "name_short" is the short version of "name", for when it turns out to be a virtual item. Returns three items: 1. A string for the part that matches with "%". If there is no match this is an empty string. 2. The directory of name_short when it was ignored for matching. Otherwise it's an empty string. 3. The length of the matching pattern.""" name_len = len(name) i = string.rfind(name, "/") # Get the tail of the name, to be used below. if i >= 0: tail = name[i + 1:] tail_len = len(tail) else: tail = name tail_len = name_len for t in patlist: pat = t["name"] pat_len = len(pat) # If the pattern doesn't have a slash, match with the tail of the name if string.find(pat, "/") < 0: str = tail str_len = tail_len # If the pattern has the "virtual" attribute, use the short name # (if it's already known the name is a virtual item, "name" already is # the short name). elif t.has_key("virtual") and t["virtual"]: str = name_short str_len = len(name_short) else: str = name str_len = name_len if pat_len > str_len: continue # pattern is longer than str i = string.find(pat, "%") if i < 0: recipe_error(rpstack, _('Missing %% in rule target "%s"') % pat) # TODO: should ignore differences between forward and backward slashes # and upper/lower case. if i > 0 and pat[0:i] != str[0:i]: continue # part before % doesn't match e = str_len - (pat_len - i - 1) if pat[i+1:] != str[e:]: continue # part after % doesn't match # TODO: use a regexp pattern to match with if t.has_key("skip") and t["skip"] == name: continue # When matching with the tail, return the directory of the short name, # this is added to the maching names. dir = '' if str == tail: si = string.rfind(name_short, "/") if si >= 0: dir = name_short[:si] return str[i:e], dir, pat_len # return the match return '', '', 0 # return without a match class Rule: def __init__(self, targetlist, build_attr, sourcelist, rpstack, commands): self.targetlist = targetlist self.build_attr = build_attr self.sourcelist = sourcelist self.rpstack = rpstack self.commands = commands self.builddir = os.getcwd() def match_target(self, name, name_short): """If "name" matches with one of the target patterns return a string for the part that matches with "%". Otherwise return an empty string. also return the length of the matching pattern.""" return _trymatch(self.rpstack, name, name_short, self.targetlist) def target2sourcelist(self, name, name_short): """Assume a target matches with "name" and return the corresponding dictlist of sources.""" return self.target2list(name, name_short, self.sourcelist) def target2targetlist(self, name, name_short): """Assume a target matches with "name" and return the corresponding dictlist of sources.""" return self.target2list(name, name_short, self.targetlist) def target2list(self, name, name_short, list): match, dir, matchlen = self.match_target(name, name_short) if match == '': raise InternalError, \ _('target2list used without matching target for "%s"') \ % name res = [] for l in list: ent = l.copy() n = string.replace(l["name"], "%", match) # if the match was on the tail of the name, prepend the directory. if dir: n = os.path.join(dir, n) ent["name"] = n res.append(ent) return res def find_rule(work, tname, sname): """Check if there is a rule for target "tname" and source "sname". These must be the short names (not expanded to a full path). Return the Rule object or None.""" for r in work.rules: tm, dir, tl = _trymatch(r.rpstack, tname, tname, r.targetlist) sm, dir, sl = _trymatch(r.rpstack, sname, sname, r.sourcelist) if tm and sm: return r return None # vim: set sw=4 sts=4 tw=79 fo+=l: .