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