65
class StringExpansion(object):
66
class BaseExpansion(object):
67
"""Base class for expansions.
69
A make expansion is the parsed representation of a string, which may
70
contain references to other elements.
74
def is_static_string(self):
75
"""Returns whether the expansion is composed of static string content.
77
This is always True for StringExpansion. It will be True for Expansion
78
only if all elements of that Expansion are static strings.
80
raise Exception('Must be implemented in child class.')
82
def functions(self, descend=False):
83
"""Obtain all functions inside this expansion.
85
This is a generator for pymake.functions.Function instances.
87
By default, this only returns functions existing as the primary
88
elements of this expansion. If `descend` is True, it will descend into
89
child expansions and extract all functions in the tree.
91
# An empty generator. Yeah, it's weird.
95
def variable_references(self, descend=False):
96
"""Obtain all variable references in this expansion.
98
This is a generator for pymake.functionsVariableRef instances.
100
To retrieve the names of variables, simply query the `vname` field on
101
the returned instances. Most of the time these will be StringExpansion
104
for f in self.functions(descend=descend):
105
if not isinstance(f, functions.VariableRef):
111
def is_filesystem_dependent(self):
112
"""Whether this expansion may query the filesystem for evaluation.
114
This effectively asks "is any function in this expansion dependent on
117
for f in self.functions(descend=True):
118
if f.is_filesystem_dependent:
124
def is_shell_dependent(self):
125
"""Whether this expansion may invoke a shell for evaluation."""
127
for f in self.functions(descend=True):
128
if isinstance(f, functions.ShellFunction):
134
class StringExpansion(BaseExpansion):
135
"""An Expansion representing a static string.
137
This essentially wraps a single str instance.
66
140
__slots__ = ('loc', 's',)
69
143
def __init__(self, s, loc):
70
144
assert isinstance(s, str)
104
182
def __repr__(self):
105
183
return "Exp<%s>(%r)" % (self.loc, self.s)
107
class Expansion(list):
109
A representation of expanded data, such as that for a recursively-expanded variable, a command, etc.
185
def __eq__(self, other):
186
"""We only compare the string contents."""
187
return self.s == other
189
def __ne__(self, other):
190
return not self.__eq__(other)
192
def to_source(self, escape_variables=False, escape_comments=False):
196
s = s.replace('#', '\\#')
199
return s.replace('$', '$$')
204
class Expansion(BaseExpansion, list):
205
"""A representation of expanded data.
207
This is effectively an ordered list of StringExpansion and
208
pymake.function.Function instances. Every item in the collection appears in
209
the same context in a make file.
112
212
__slots__ = ('loc',)
232
332
def resolvesplit(self, makefile, variables, setting=[]):
233
333
return self.resolvestr(makefile, variables, setting).split()
336
def is_static_string(self):
337
"""An Expansion is static if all its components are strings, not
339
for e, is_func in self:
345
def functions(self, descend=False):
346
for e, is_func in self:
351
for exp in e.expansions(descend=True):
352
for f in exp.functions(descend=True):
235
355
def __repr__(self):
236
356
return "<Expansion with elements: %r>" % ([e for e, isfunc in self],)
358
def to_source(self, escape_variables=False, escape_comments=False):
360
for e, is_func in self:
362
parts.append(e.to_source())
366
parts.append(e.replace('$', '$$'))
371
return ''.join(parts)
373
def __eq__(self, other):
374
if not isinstance(other, (Expansion, StringExpansion)):
377
# Expansions are equivalent if adjacent string literals normalize to
378
# the same value. So, we must normalize before any comparisons are
380
a = self.clone().finish()
382
if isinstance(other, StringExpansion):
383
if isinstance(a, StringExpansion):
386
# A normalized Expansion != StringExpansion.
389
b = other.clone().finish()
391
# b could be a StringExpansion now.
392
if isinstance(b, StringExpansion):
393
if isinstance(a, StringExpansion):
396
# Our normalized Expansion != normalized StringExpansion.
402
for i in xrange(len(self)):
406
if is_func1 != is_func2:
409
if type(e1) != type(e2):
417
def __ne__(self, other):
418
return not self.__eq__(other)
238
420
class Variables(object):
240
422
A mapping from variable names to variables. Variables have flavor, source, and value. The value is an
1002
1184
search += [util.normaljoin(dir, self.target).replace('\\', '/')
1003
1185
for dir in makefile.getvpath(self.target)]
1187
targetandtime = self.searchinlocs(makefile, search)
1188
if targetandtime is not None:
1189
(self.vpathtarget, self.mtime) = targetandtime
1192
self.vpathtarget = self.target
1195
def searchinlocs(self, makefile, locs):
1197
Look in the given locations relative to the makefile working directory
1198
for a file. Return a pair of the target and the mtime if found, None
1006
1202
fspath = util.normaljoin(makefile.workdir, t).replace('\\', '/')
1007
1203
mtime = getmtime(fspath)
1008
1204
# _log.info("Searching %s ... checking %s ... mtime %r" % (t, fspath, mtime))
1009
1205
if mtime is not None:
1010
self.vpathtarget = t
1014
self.vpathtarget = self.target
1017
1210
def beingremade(self):
1019
When we remake ourself, we need to reset our mtime and vpathtarget.
1021
We store our old mtime so that $? can calculate out-of-date prerequisites.
1212
When we remake ourself, we have to drop any vpath prefixes.
1023
self.realmtime = self.mtime
1025
1214
self.vpathtarget = self.target
1026
1215
self.wasremade = True
1028
1217
def notifydone(self, makefile):
1029
1218
assert self._state == MAKESTATE_WORKING, "State was %s" % self._state
1219
# If we were remade then resolve mtime again
1221
targetandtime = self.searchinlocs(makefile, [self.target])
1222
if targetandtime is not None:
1223
(_, self.mtime) = targetandtime
1031
1227
self._state = MAKESTATE_FINISHED
1032
1228
for cb in self._callbacks:
1133
1329
prtargets = [makefile.gettarget(p) for p in prerequisites]
1134
1330
prall = [pt.vpathtarget for pt in prtargets]
1135
1331
proutofdate = [pt.vpathtarget for pt in withoutdups(prtargets)
1136
if target.realmtime is None or mtimeislater(pt.mtime, target.realmtime)]
1332
if target.mtime is None or mtimeislater(pt.mtime, target.mtime)]
1138
1334
setautomatic(v, '@', [target.vpathtarget])
1204
1400
_CommandWrapper.__init__(self, cline, ignoreErrors, loc, context,
1206
1402
# get the module and method to call
1207
parts, badchar = process.clinetoargv(cline)
1403
parts, badchar = process.clinetoargv(cline, blacklist_gray=False)
1208
1404
if parts is None:
1209
1405
raise DataError("native command '%s': shell metacharacter '%s' in command line" % (cline, badchar), self.loc)
1210
1406
if len(parts) < 2:
1542
1738
if len(np.rules):
1543
1739
self.context = process.getcontext(1)
1741
flavor, source, value = self.variables.get('.DEFAULT_GOAL')
1742
if value is not None:
1743
self.defaulttarget = value.resolvestr(self, self.variables, ['.DEFAULT_GOAL']).strip()
1545
1745
self.error = False
1547
1747
def include(self, path, required=True, weak=False, loc=None):