1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186
|
# 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:
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:
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] == str[0:i]:
# part before % is empty or matches
e = str_len - (pat_len - i - 1)
if pat[i+1:] == str[e:]:
# part after % matches
match = 1
if not match and str == name:
# try matching with short name as well
str = name_short
str_len = len(name_short)
if pat_len > str_len:
continue # pattern is longer than str
if i == 0 or pat[0:i] == str[0:i]:
# part before % is empty or matches
e = str_len - (pat_len - i - 1)
if pat[i+1:] == str[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.
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.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, 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 et sts=4 tw=79 fo+=l:
|