# Part of the A-A-P recipe executive: A Rule object # 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 Rule object contains: # default - boolean: default rule # sourceexists - boolean: source files must exist # 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 # buildrecdict - recdict for build commands (for a child recipe) # scope - "normal", "local" or "global" # # A Rule is also used for a typerule. The only difference is that instead of # patterns file types are used. # # Illustration: # :rule {default} targetlist : {build_attr} sourcelist # commands import string, os, os.path from Error import * from Util import fname_fold 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.""" # For non-Unix systems "/" and "\" are treated equally and case is ignored. # The user must do this for the pattern. name = fname_fold(name) name_short = fname_fold(name_short) 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: s = 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"]: s = name_short str_len = len(name_short) else: s = name str_len = name_len if pat_len > str_len: continue # pattern is longer than s i = string.find(pat, "%") if i < 0: from Process import recipe_error recipe_error(rpstack, _('Missing %% in rule target "%s"') % pat) # TODO: should ignore differences between forward and backward slashes # and upper/lower case. match = 0 if i == 0 or pat[0:i] == s[0:i]: # part before % is empty or matches e = str_len - (pat_len - i - 1) if pat[i+1:] == s[e:]: # part after % matches match = 1 if not match and s == name: # try matching with short name as well s = name_short str_len = len(name_short) if pat_len > str_len: continue # pattern is longer than s if i == 0 or pat[0:i] == s[0:i]: # part before % is empty or matches e = str_len - (pat_len - i - 1) if pat[i+1:] == s[e:]: # part after % matches match = 1 if not match: continue # 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. adir = '' if s == tail: si = string.rfind(name_short, "/") if si >= 0: adir = name_short[:si] return s[i:e], adir, pat_len # return the match return '', '', 0 # return without a match class Rule: def __init__(self, targetlist, build_attr, sourcelist, rpstack, commands): self.default = 0 self.sourceexists = 0 self.targetlist = targetlist self.build_attr = build_attr self.sourcelist = sourcelist self.rpstack = rpstack self.commands = commands self.builddir = os.getcwd() self.buildrecdict = None self.scope = "normal" 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, adir, 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 adir: n = os.path.join(adir, 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, adir, tl = _trymatch(r.rpstack, tname, tname, r.targetlist) sm, adir, sl = _trymatch(r.rpstack, sname, sname, r.sourcelist) if tm and sm: return r return None # vim: set sw=4 et sts=4 tw=79 fo+=l: