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)
102
180
return self.s, False
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.
112
__slots__ = ('loc', 'hasfunc')
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.
115
215
def __init__(self, loc=None):
116
216
# A list of (element, isfunc) tuples
117
217
# element is either a string or a function
122
221
def fromstring(s, path):
181
278
def finish(self):
185
return StringExpansion(''.join([i for i, isfunc in self]), self.loc)
279
# Merge any adjacent literal strings:
282
for (e, isfunc) in self:
287
elements.append((s, False))
289
elements.append((e, True))
294
# This can only happen if there were no function elements.
295
return StringExpansion(''.join(strings), self.loc)
300
elements.append((s, False))
302
if len(elements) < len(self):
187
307
def resolve(self, makefile, variables, fd, setting=[]):
212
332
def resolvesplit(self, makefile, variables, setting=[]):
213
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):
215
355
def __repr__(self):
216
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)
218
420
class Variables(object):
220
422
A mapping from variable names to variables. Variables have flavor, source, and value. The value is an
695
897
for d, weak in self.deps:
696
898
if mtimeislater(d.mtime, self.target.mtime):
697
self.target.beingremade()
900
self.target.beingremade()
902
_log.info("%sNot remaking %s ubecause it would have no effect, even though %s is newer.", indent, self.target.target, d.target)
979
1184
search += [util.normaljoin(dir, self.target).replace('\\', '/')
980
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
983
1202
fspath = util.normaljoin(makefile.workdir, t).replace('\\', '/')
984
1203
mtime = getmtime(fspath)
985
1204
# _log.info("Searching %s ... checking %s ... mtime %r" % (t, fspath, mtime))
986
1205
if mtime is not None:
991
self.vpathtarget = self.target
994
1210
def beingremade(self):
996
When we remake ourself, we need to reset our mtime and vpathtarget.
998
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.
1000
self.realmtime = self.mtime
1002
1214
self.vpathtarget = self.target
1003
1215
self.wasremade = True
1005
1217
def notifydone(self, makefile):
1006
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
1008
1227
self._state = MAKESTATE_FINISHED
1009
1228
for cb in self._callbacks:
1110
1329
prtargets = [makefile.gettarget(p) for p in prerequisites]
1111
1330
prall = [pt.vpathtarget for pt in prtargets]
1112
1331
proutofdate = [pt.vpathtarget for pt in withoutdups(prtargets)
1113
if target.realmtime is None or mtimeislater(pt.mtime, target.realmtime)]
1332
if target.mtime is None or mtimeislater(pt.mtime, target.mtime)]
1115
1334
setautomatic(v, '@', [target.vpathtarget])
1181
1400
_CommandWrapper.__init__(self, cline, ignoreErrors, loc, context,
1183
1402
# get the module and method to call
1184
parts, badchar = process.clinetoargv(cline)
1403
parts, badchar = process.clinetoargv(cline, blacklist_gray=False)
1185
1404
if parts is None:
1186
1405
raise DataError("native command '%s': shell metacharacter '%s' in command line" % (cline, badchar), self.loc)
1187
1406
if len(parts) < 2:
1519
1738
if len(np.rules):
1520
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()
1522
1745
self.error = False
1524
1747
def include(self, path, required=True, weak=False, loc=None):