2
A representation of makefile data structures.
5
import logging, re, os, sys
6
import parserdata, parser, functions, process, util, implicit
7
from cStringIO import StringIO
9
_log = logging.getLogger('pymake.data')
11
class DataError(util.MakeError):
14
class ResolutionError(DataError):
16
Raised when dependency resolution fails, either due to recursion or to missing
17
prerequisites.This is separately catchable so that implicit rule search can try things
18
without having to commit.
29
def mtimeislater(deptime, targettime):
31
Is the mtime of the dependency later than the target?
36
if targettime is None:
38
return deptime > targettime
48
if s.startswith('./'):
50
return st if st != '' else '.'
53
def stripdotslashes(sl):
55
yield stripdotslash(s)
58
return ''.ljust(len(stack) - 1)
60
def _if_else(c, t, f):
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.
140
__slots__ = ('loc', 's',)
143
def __init__(self, s, loc):
144
assert isinstance(s, str)
149
self.s = self.s.lstrip()
152
self.s = self.s.rstrip()
157
def resolve(self, i, j, fd, k=None):
160
def resolvestr(self, i, j, k=None):
163
def resolvesplit(self, i, j, k=None):
164
return self.s.split()
167
e = Expansion(self.loc)
172
def is_static_string(self):
178
def __getitem__(self, i):
183
return "Exp<%s>(%r)" % (self.loc, self.s)
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.
215
def __init__(self, loc=None):
216
# A list of (element, isfunc) tuples
217
# element is either a string or a function
221
def fromstring(s, path):
222
return StringExpansion(s, parserdata.Location(path, 1, 0))
229
def appendstr(self, s):
230
assert isinstance(s, str)
234
self.append((s, False))
236
def appendfunc(self, func):
237
assert isinstance(func, functions.Function)
238
self.append((func, True))
241
"""Concatenate the other expansion on to this one."""
248
return (not len(self)) or self[0] == ('', False)
251
"""Strip leading literal whitespace from this expansion."""
265
"""Strip trailing literal whitespace from this expansion."""
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):
307
def resolve(self, makefile, variables, fd, setting=[]):
309
Resolve this variable into a value, by interpolating the value
312
@param setting (Variable instance) the variable currently
313
being set, if any. Setting variables must avoid self-referential
316
assert isinstance(makefile, Makefile)
317
assert isinstance(variables, Variables)
318
assert isinstance(setting, list)
320
for e, isfunc in self:
322
e.resolve(makefile, variables, fd, setting)
324
assert isinstance(e, str)
327
def resolvestr(self, makefile, variables, setting=[]):
329
self.resolve(makefile, variables, fd, setting)
332
def resolvesplit(self, makefile, variables, setting=[]):
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):
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)
420
class Variables(object):
422
A mapping from variable names to variables. Variables have flavor, source, and value. The value is an
426
__slots__ = ('parent', '_map')
433
SOURCE_COMMANDLINE = 1
435
SOURCE_ENVIRONMENT = 3
439
def __init__(self, parent=None):
440
self._map = {} # vname -> flavor, source, valuestr, valueexp
443
def readfromenvironment(self, env):
444
for k, v in env.iteritems():
445
self.set(k, self.FLAVOR_SIMPLE, self.SOURCE_ENVIRONMENT, v)
447
def get(self, name, expand=True):
449
Get the value of a named variable. Returns a tuple (flavor, source, value)
451
If the variable is not present, returns (None, None, None)
453
@param expand If true, the value will be returned as an expansion. If false,
454
it will be returned as an unexpanded string.
456
flavor, source, valuestr, valueexp = self._map.get(name, (None, None, None, None))
457
if flavor is not None:
458
if expand and flavor != self.FLAVOR_SIMPLE and valueexp is None:
459
d = parser.Data.fromstring(valuestr, parserdata.Location("Expansion of variables '%s'" % (name,), 1, 0))
460
valueexp, t, o = parser.parsemakesyntax(d, 0, (), parser.iterdata)
461
self._map[name] = flavor, source, valuestr, valueexp
463
if flavor == self.FLAVOR_APPEND:
465
pflavor, psource, pvalue = self.parent.get(name, expand)
467
pflavor, psource, pvalue = None, None, None
470
flavor = self.FLAVOR_RECURSIVE
474
# TODO: log a warning?
475
return pflavor, psource, pvalue
478
return pflavor, psource, pvalue + ' ' + valuestr
480
pvalue = pvalue.clone()
481
pvalue.appendstr(' ')
482
pvalue.concat(valueexp)
484
return pflavor, psource, pvalue
487
return flavor, source, valuestr
489
if flavor == self.FLAVOR_RECURSIVE:
492
val = Expansion.fromstring(valuestr, "Expansion of variable '%s'" % (name,))
494
return flavor, source, val
496
if self.parent is not None:
497
return self.parent.get(name, expand)
499
return (None, None, None)
501
def set(self, name, flavor, source, value):
502
assert flavor in (self.FLAVOR_RECURSIVE, self.FLAVOR_SIMPLE)
503
assert source in (self.SOURCE_OVERRIDE, self.SOURCE_COMMANDLINE, self.SOURCE_MAKEFILE, self.SOURCE_ENVIRONMENT, self.SOURCE_AUTOMATIC, self.SOURCE_IMPLICIT)
504
assert isinstance(value, str), "expected str, got %s" % type(value)
506
prevflavor, prevsource, prevvalue = self.get(name)
507
if prevsource is not None and source > prevsource:
508
# TODO: give a location for this warning
509
_log.info("not setting variable '%s', set by higher-priority source to value '%s'" % (name, prevvalue))
512
self._map[name] = flavor, source, value, None
514
def append(self, name, source, value, variables, makefile):
515
assert source in (self.SOURCE_OVERRIDE, self.SOURCE_MAKEFILE, self.SOURCE_AUTOMATIC)
516
assert isinstance(value, str)
518
if name not in self._map:
519
self._map[name] = self.FLAVOR_APPEND, source, value, None
522
prevflavor, prevsource, prevvalue, valueexp = self._map[name]
523
if source > prevsource:
524
# TODO: log a warning?
527
if prevflavor == self.FLAVOR_SIMPLE:
528
d = parser.Data.fromstring(value, parserdata.Location("Expansion of variables '%s'" % (name,), 1, 0))
529
valueexp, t, o = parser.parsemakesyntax(d, 0, (), parser.iterdata)
531
val = valueexp.resolvestr(makefile, variables, [name])
532
self._map[name] = prevflavor, prevsource, prevvalue + ' ' + val, None
535
newvalue = prevvalue + ' ' + value
536
self._map[name] = prevflavor, prevsource, newvalue, None
538
def merge(self, other):
539
assert isinstance(other, Variables)
540
for k, flavor, source, value in other:
541
self.set(k, flavor, source, value)
544
for k, (flavor, source, value, valueexp) in self._map.iteritems():
545
yield k, flavor, source, value
547
def __contains__(self, item):
548
return item in self._map
550
class Pattern(object):
552
A pattern is a string, possibly with a % substitution character. From the GNU make manual:
554
'%' characters in pattern rules can be quoted with precending backslashes ('\'). Backslashes that
555
would otherwise quote '%' charcters can be quoted with more backslashes. Backslashes that
556
quote '%' characters or other backslashes are removed from the pattern before it is compared t
557
file names or has a stem substituted into it. Backslashes that are not in danger of quoting '%'
558
characters go unmolested. For example, the pattern the\%weird\\%pattern\\ has `the%weird\' preceding
559
the operative '%' character, and 'pattern\\' following it. The final two backslashes are left alone
560
because they cannot affect any '%' character.
562
This insane behavior probably doesn't matter, but we're compatible just for shits and giggles.
567
def __init__(self, s):
583
self.data = (''.join(r), s[i+1:])
589
# This is different than (s,) because \% and \\ have been unescaped. Parsing patterns is
591
self.data = (''.join(r),)
593
def ismatchany(self):
594
return self.data == ('','')
597
return len(self.data) == 2
600
return self.data.__hash__()
603
assert isinstance(o, Pattern)
604
return self.data == o.data
607
assert not self.ispattern()
611
return self.data[0].find('/') != -1 or self.data[1].find('/') != -1
613
def match(self, word):
615
Match this search pattern against a word (string).
617
@returns None if the word doesn't match, or the matching stem.
618
If this is a %-less pattern, the stem will always be ''
629
if len(word) >= l1 + l2 and word.startswith(d0) and word.endswith(d1):
636
def resolve(self, dir, stem):
638
return dir + self.data[0] + stem + self.data[1]
642
def subst(self, replacement, word, mustmatch):
644
Given a word, replace the current pattern with the replacement pattern, a la 'patsubst'
646
@param mustmatch If true and this pattern doesn't match the word, throw a DataError. Otherwise
647
return word unchanged.
649
assert isinstance(replacement, str)
651
stem = self.match(word)
654
raise DataError("target '%s' doesn't match pattern" % (word,))
657
if not self.ispattern():
658
# if we're not a pattern, the replacement is not parsed as a pattern either
661
return Pattern(replacement).resolve('', stem)
664
return "<Pattern with data %r>" % (self.data,)
666
_backre = re.compile(r'[%\\]')
668
if not self.ispattern():
669
return self._backre.sub(r'\\\1', self.data[0])
671
return self._backre.sub(r'\\\1', self.data[0]) + '%' + self.data[1]
673
class RemakeTargetSerially(object):
674
__slots__ = ('target', 'makefile', 'indent', 'rlist')
676
def __init__(self, target, makefile, indent, rlist):
678
self.makefile = makefile
681
self.commandscb(False)
683
def resolvecb(self, error, didanything):
684
assert error in (True, False)
687
self.target.didanything = True
690
self.target.error = True
691
self.makefile.error = True
692
if not self.makefile.keepgoing:
693
self.target.notifydone(self.makefile)
696
# don't run the commands!
698
self.commandscb(error=False)
700
self.rlist.pop(0).runcommands(self.indent, self.commandscb)
702
def commandscb(self, error):
703
assert error in (True, False)
706
self.target.error = True
707
self.makefile.error = True
709
if self.target.error and not self.makefile.keepgoing:
710
self.target.notifydone(self.makefile)
713
if not len(self.rlist):
714
self.target.notifydone(self.makefile)
716
self.rlist[0].resolvedeps(True, self.resolvecb)
718
class RemakeTargetParallel(object):
719
__slots__ = ('target', 'makefile', 'indent', 'rlist', 'rulesremaining', 'currunning')
721
def __init__(self, target, makefile, indent, rlist):
723
self.makefile = makefile
727
self.rulesremaining = len(rlist)
728
self.currunning = False
731
makefile.context.defer(self.doresolve, r)
733
def doresolve(self, r):
734
if self.makefile.error and not self.makefile.keepgoing:
736
self.resolvecb(True, False)
738
r.resolvedeps(False, self.resolvecb)
740
def resolvecb(self, error, didanything):
741
assert error in (True, False)
744
self.target.error = True
747
self.target.didanything = True
749
self.rulesremaining -= 1
751
# commandscb takes care of the details if we're currently building
759
assert not self.currunning
761
if self.makefile.error and not self.makefile.keepgoing:
764
while len(self.rlist) and self.rlist[0].error:
767
if not len(self.rlist):
768
if not self.rulesremaining:
769
self.target.notifydone(self.makefile)
772
if self.rlist[0].depsremaining != 0:
775
self.currunning = True
776
self.rlist.pop(0).runcommands(self.indent, self.commandscb)
778
def commandscb(self, error):
779
assert error in (True, False)
781
self.target.error = True
782
self.makefile.error = True
784
assert self.currunning
785
self.currunning = False
788
class RemakeRuleContext(object):
789
def __init__(self, target, makefile, rule, deps,
790
targetstack, avoidremakeloop):
792
self.makefile = makefile
795
self.targetstack = targetstack
796
self.avoidremakeloop = avoidremakeloop
800
self.depsremaining = len(deps) + 1
803
def resolvedeps(self, serial, cb):
805
self.didanything = False
807
self._resolvedepsserial()
809
self._resolvedepsparallel()
811
def _weakdepfinishedserial(self, error, didanything):
814
self._depfinishedserial(False, didanything)
816
def _depfinishedserial(self, error, didanything):
817
assert error in (True, False)
820
self.didanything = True
824
if not self.makefile.keepgoing:
825
self.resolvecb(error=True, didanything=self.didanything)
828
if len(self.resolvelist):
829
dep, weak = self.resolvelist.pop(0)
830
self.makefile.context.defer(dep.make,
831
self.makefile, self.targetstack, weak and self._weakdepfinishedserial or self._depfinishedserial)
833
self.resolvecb(error=self.error, didanything=self.didanything)
835
def _resolvedepsserial(self):
836
self.resolvelist = list(self.deps)
837
self._depfinishedserial(False, False)
839
def _startdepparallel(self, d):
840
if self.makefile.error:
841
depfinished(True, False)
844
dep.make(self.makefile, self.targetstack, weak and self._weakdepfinishedparallel or self._depfinishedparallel)
846
def _weakdepfinishedparallel(self, error, didanything):
849
self._depfinishedparallel(False, didanything)
851
def _depfinishedparallel(self, error, didanything):
852
assert error in (True, False)
855
print "<%s>: Found error" % self.target.target
858
self.didanything = True
860
self.depsremaining -= 1
861
if self.depsremaining == 0:
862
self.resolvecb(error=self.error, didanything=self.didanything)
864
def _resolvedepsparallel(self):
865
self.depsremaining -= 1
866
if self.depsremaining == 0:
867
self.resolvecb(error=self.error, didanything=self.didanything)
870
self.didanything = False
873
self.makefile.context.defer(self._startdepparallel, d)
875
def _commandcb(self, error):
876
assert error in (True, False)
879
self.runcb(error=True)
882
if len(self.commands):
883
self.commands.pop(0)(self._commandcb)
885
self.runcb(error=False)
887
def runcommands(self, indent, cb):
888
assert not self.running
893
if self.rule is None or not len(self.rule.commands):
894
if self.target.mtime is None:
895
self.target.beingremade()
897
for d, weak in self.deps:
898
if mtimeislater(d.mtime, self.target.mtime):
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)
907
if self.rule.doublecolon:
908
if len(self.deps) == 0:
909
if self.avoidremakeloop:
910
_log.info("%sNot remaking %s using rule at %s because it would introduce an infinite loop.", indent, self.target.target, self.rule.loc)
916
_log.info("%sRemaking %s using rule at %s: weak dependency was not found.", indent, self.target.target, self.rule.loc)
918
if self.target.mtime is None:
920
_log.info("%sRemaking %s using rule at %s: target doesn't exist or is a forced target", indent, self.target.target, self.rule.loc)
923
if self.rule.doublecolon:
924
if len(self.deps) == 0:
925
_log.info("%sRemaking %s using rule at %s because there are no prerequisites listed for a double-colon rule.", indent, self.target.target, self.rule.loc)
929
for d, weak in self.deps:
930
if mtimeislater(d.mtime, self.target.mtime):
931
_log.info("%sRemaking %s using rule at %s because %s is newer.", indent, self.target.target, self.rule.loc, d.target)
936
self.target.beingremade()
937
self.target.didanything = True
939
self.commands = [c for c in self.rule.getcommands(self.target, self.makefile)]
940
except util.MakeError, e:
946
self._commandcb(False)
951
MAKESTATE_FINISHED = 1
952
MAKESTATE_WORKING = 2
954
class Target(object):
956
An actual (non-pattern) target.
958
It holds target-specific variables and a list of rules. It may also point to a parent
959
PatternTarget, if this target is being created by an implicit rule.
961
The rules associated with this target may be Rule instances or, in the case of static pattern
962
rules, PatternRule instances.
967
def __init__(self, target, makefile):
968
assert isinstance(target, str)
970
self.vpathtarget = None
972
self.variables = Variables(makefile.variables)
973
self.explicit = False
974
self._state = MAKESTATE_NONE
976
def addrule(self, rule):
977
assert isinstance(rule, (Rule, PatternRuleInstance))
978
if len(self.rules) and rule.doublecolon != self.rules[0].doublecolon:
979
raise DataError("Cannot have single- and double-colon rules for the same target. Prior rule location: %s" % self.rules[0].loc, rule.loc)
981
if isinstance(rule, PatternRuleInstance):
982
if len(rule.prule.targetpatterns) != 1:
983
raise DataError("Static pattern rules must only have one target pattern", rule.prule.loc)
984
if rule.prule.targetpatterns[0].match(self.target) is None:
985
raise DataError("Static pattern rule doesn't match target '%s'" % self.target, rule.loc)
987
self.rules.append(rule)
989
def isdoublecolon(self):
990
return self.rules[0].doublecolon
992
def isphony(self, makefile):
993
"""Is this a phony target? We don't check for existence of phony targets."""
994
return makefile.gettarget('.PHONY').hasdependency(self.target)
996
def hasdependency(self, t):
997
for rule in self.rules:
998
if t in rule.prerequisites:
1003
def resolveimplicitrule(self, makefile, targetstack, rulestack):
1005
Try to resolve an implicit rule to build this target.
1007
# The steps in the GNU make manual Implicit-Rule-Search.html are very detailed. I hope they can be trusted.
1009
indent = getindent(targetstack)
1011
_log.info("%sSearching for implicit rule to make '%s'", indent, self.target)
1013
dir, s, file = util.strrpartition(self.target, '/')
1016
candidates = [] # list of PatternRuleInstance
1018
hasmatch = util.any((r.hasspecificmatch(file) for r in makefile.implicitrules))
1020
for r in makefile.implicitrules:
1022
_log.info("%s %s: Avoiding implicit rule recursion", indent, r.loc)
1025
if not len(r.commands):
1028
for ri in r.matchesfor(dir, file, hasmatch):
1029
candidates.append(ri)
1033
for r in candidates:
1035
for p in r.prerequisites:
1036
t = makefile.gettarget(p)
1037
t.resolvevpath(makefile)
1038
if not t.explicit and t.mtime is None:
1042
if depfailed is not None:
1044
_log.info("%s Terminal rule at %s doesn't match: prerequisite '%s' not mentioned and doesn't exist.", indent, r.loc, depfailed)
1046
newcandidates.append(r)
1049
_log.info("%sFound implicit rule at %s for target '%s'", indent, r.loc, self.target)
1050
self.rules.append(r)
1053
# Try again, but this time with chaining and without terminal (double-colon) rules
1055
for r in newcandidates:
1056
newrulestack = rulestack + [r.prule]
1059
for p in r.prerequisites:
1060
t = makefile.gettarget(p)
1062
t.resolvedeps(makefile, targetstack, newrulestack, True)
1063
except ResolutionError:
1067
if depfailed is not None:
1068
_log.info("%s Rule at %s doesn't match: prerequisite '%s' could not be made.", indent, r.loc, depfailed)
1071
_log.info("%sFound implicit rule at %s for target '%s'", indent, r.loc, self.target)
1072
self.rules.append(r)
1075
_log.info("%sCouldn't find implicit rule to remake '%s'", indent, self.target)
1077
def ruleswithcommands(self):
1078
"The number of rules with commands"
1079
return reduce(lambda i, rule: i + (len(rule.commands) > 0), self.rules, 0)
1081
def resolvedeps(self, makefile, targetstack, rulestack, recursive):
1083
Resolve the actual path of this target, using vpath if necessary.
1085
Recursively resolve dependencies of this target. This means finding implicit
1086
rules which match the target, if appropriate.
1088
Figure out whether this target needs to be rebuild, and set self.outofdate
1091
@param targetstack is the current stack of dependencies being resolved. If
1092
this target is already in targetstack, bail to prevent infinite
1094
@param rulestack is the current stack of implicit rules being used to resolve
1095
dependencies. A rule chain cannot use the same implicit rule twice.
1097
assert makefile.parsingfinished
1099
if self.target in targetstack:
1100
raise ResolutionError("Recursive dependency: %s -> %s" % (
1101
" -> ".join(targetstack), self.target))
1103
targetstack = targetstack + [self.target]
1105
indent = getindent(targetstack)
1107
_log.info("%sConsidering target '%s'", indent, self.target)
1109
self.resolvevpath(makefile)
1111
# Sanity-check our rules. If we're single-colon, only one rule should have commands
1112
ruleswithcommands = self.ruleswithcommands()
1113
if len(self.rules) and not self.isdoublecolon():
1114
if ruleswithcommands > 1:
1115
# In GNU make this is a warning, not an error. I'm going to be stricter.
1116
# TODO: provide locations
1117
raise DataError("Target '%s' has multiple rules with commands." % self.target)
1119
if ruleswithcommands == 0:
1120
self.resolveimplicitrule(makefile, targetstack, rulestack)
1122
# If a target is mentioned, but doesn't exist, has no commands and no
1123
# prerequisites, it is special and exists just to say that targets which
1124
# depend on it are always out of date. This is like .FORCE but more
1125
# compatible with other makes.
1126
# Otherwise, we don't know how to make it.
1127
if not len(self.rules) and self.mtime is None and not util.any((len(rule.prerequisites) > 0
1128
for rule in self.rules)):
1129
raise ResolutionError("No rule to make target '%s' needed by %r" % (self.target,
1133
for r in self.rules:
1134
newrulestack = rulestack + [r]
1135
for d in r.prerequisites:
1136
dt = makefile.gettarget(d)
1140
dt.resolvedeps(makefile, targetstack, newrulestack, True)
1142
for v in makefile.getpatternvariablesfor(self.target):
1143
self.variables.merge(v)
1145
def resolvevpath(self, makefile):
1146
if self.vpathtarget is not None:
1149
if self.isphony(makefile):
1150
self.vpathtarget = self.target
1154
if self.target.startswith('-l'):
1155
stem = self.target[2:]
1156
f, s, e = makefile.variables.get('.LIBPATTERNS')
1158
libpatterns = [Pattern(stripdotslash(s)) for s in e.resolvesplit(makefile, makefile.variables)]
1159
if len(libpatterns):
1161
searchdirs.extend(makefile.getvpath(self.target))
1163
for lp in libpatterns:
1164
if not lp.ispattern():
1165
raise DataError('.LIBPATTERNS contains a non-pattern')
1167
libname = lp.resolve('', stem)
1169
for dir in searchdirs:
1170
libpath = util.normaljoin(dir, libname).replace('\\', '/')
1171
fspath = util.normaljoin(makefile.workdir, libpath)
1172
mtime = getmtime(fspath)
1173
if mtime is not None:
1174
self.vpathtarget = libpath
1178
self.vpathtarget = self.target
1182
search = [self.target]
1183
if not os.path.isabs(self.target):
1184
search += [util.normaljoin(dir, self.target).replace('\\', '/')
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
1202
fspath = util.normaljoin(makefile.workdir, t).replace('\\', '/')
1203
mtime = getmtime(fspath)
1204
# _log.info("Searching %s ... checking %s ... mtime %r" % (t, fspath, mtime))
1205
if mtime is not None:
1210
def beingremade(self):
1212
When we remake ourself, we have to drop any vpath prefixes.
1214
self.vpathtarget = self.target
1215
self.wasremade = True
1217
def notifydone(self, makefile):
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
1227
self._state = MAKESTATE_FINISHED
1228
for cb in self._callbacks:
1229
makefile.context.defer(cb, error=self.error, didanything=self.didanything)
1232
def make(self, makefile, targetstack, cb, avoidremakeloop=False, printerror=True):
1234
If we are out of date, asynchronously make ourself. This is a multi-stage process, mostly handled
1235
by the helper objects RemakeTargetSerially, RemakeTargetParallel,
1236
RemakeRuleContext. These helper objects should keep us from developing
1237
any cyclical dependencies.
1239
* resolve dependencies (synchronous)
1240
* gather a list of rules to execute and related dependencies (synchronous)
1241
* for each rule (in parallel)
1242
** remake dependencies (asynchronous)
1243
** build list of commands to execute (synchronous)
1244
** execute each command (asynchronous)
1245
* asynchronously notify when all rules are complete
1247
@param cb A callback function to notify when remaking is finished. It is called
1248
thusly: callback(error=True/False, didanything=True/False)
1249
If there is no asynchronous activity to perform, the callback may be called directly.
1252
serial = makefile.context.jcount == 1
1254
if self._state == MAKESTATE_FINISHED:
1255
cb(error=self.error, didanything=self.didanything)
1258
if self._state == MAKESTATE_WORKING:
1260
self._callbacks.append(cb)
1263
assert self._state == MAKESTATE_NONE
1265
self._state = MAKESTATE_WORKING
1266
self._callbacks = [cb]
1268
self.didanything = False
1270
indent = getindent(targetstack)
1273
self.resolvedeps(makefile, targetstack, [], False)
1274
except util.MakeError, e:
1278
self.notifydone(makefile)
1281
assert self.vpathtarget is not None, "Target was never resolved!"
1282
if not len(self.rules):
1283
self.notifydone(makefile)
1286
if self.isdoublecolon():
1287
rulelist = [RemakeRuleContext(self, makefile, r, [(makefile.gettarget(p), False) for p in r.prerequisites], targetstack, avoidremakeloop) for r in self.rules]
1292
for r in self.rules:
1293
rdeps = [(makefile.gettarget(p), r.weakdeps) for p in r.prerequisites]
1295
assert commandrule is None
1297
# The dependencies of the command rule are resolved before other dependencies,
1298
# no matter the ordering of the other no-command rules
1299
alldeps[0:0] = rdeps
1301
alldeps.extend(rdeps)
1303
rulelist = [RemakeRuleContext(self, makefile, commandrule, alldeps, targetstack, avoidremakeloop)]
1305
targetstack = targetstack + [self.target]
1308
RemakeTargetSerially(self, makefile, indent, rulelist)
1310
RemakeTargetParallel(self, makefile, indent, rulelist)
1313
d, s, f = util.strrpartition(p, '/')
1320
d, s, f = util.strrpartition(p, '/')
1323
def setautomatic(v, name, plist):
1324
v.set(name, Variables.FLAVOR_SIMPLE, Variables.SOURCE_AUTOMATIC, ' '.join(plist))
1325
v.set(name + 'D', Variables.FLAVOR_SIMPLE, Variables.SOURCE_AUTOMATIC, ' '.join((dirpart(p) for p in plist)))
1326
v.set(name + 'F', Variables.FLAVOR_SIMPLE, Variables.SOURCE_AUTOMATIC, ' '.join((filepart(p) for p in plist)))
1328
def setautomaticvariables(v, makefile, target, prerequisites):
1329
prtargets = [makefile.gettarget(p) for p in prerequisites]
1330
prall = [pt.vpathtarget for pt in prtargets]
1331
proutofdate = [pt.vpathtarget for pt in withoutdups(prtargets)
1332
if target.mtime is None or mtimeislater(pt.mtime, target.mtime)]
1334
setautomatic(v, '@', [target.vpathtarget])
1336
setautomatic(v, '<', [prall[0]])
1338
setautomatic(v, '?', proutofdate)
1339
setautomatic(v, '^', list(withoutdups(prall)))
1340
setautomatic(v, '+', prall)
1342
def splitcommand(command):
1344
Using the esoteric rules, split command lines by unescaped newlines.
1348
while i < len(command):
1353
yield command[start:i]
1361
yield command[start:i]
1363
def findmodifiers(command):
1365
Find any of +-@% prefixed on the command.
1366
@returns (command, isHidden, isRecursive, ignoreErrors, isNative)
1371
ignoreErrors = False
1374
realcommand = command.lstrip(' \t\n@+-%')
1375
modset = set(command[:-len(realcommand)])
1376
return realcommand, '@' in modset, '+' in modset, '-' in modset, '%' in modset
1378
class _CommandWrapper(object):
1379
def __init__(self, cline, ignoreErrors, loc, context, **kwargs):
1380
self.ignoreErrors = ignoreErrors
1383
self.kwargs = kwargs
1384
self.context = context
1387
if res != 0 and not self.ignoreErrors:
1388
print "%s: command '%s' failed, return code %i" % (self.loc, self.cline, res)
1389
self.usercb(error=True)
1391
self.usercb(error=False)
1393
def __call__(self, cb):
1395
process.call(self.cline, loc=self.loc, cb=self._cb, context=self.context, **self.kwargs)
1397
class _NativeWrapper(_CommandWrapper):
1398
def __init__(self, cline, ignoreErrors, loc, context,
1399
pycommandpath, **kwargs):
1400
_CommandWrapper.__init__(self, cline, ignoreErrors, loc, context,
1402
# get the module and method to call
1403
parts, badchar = process.clinetoargv(cline, blacklist_gray=False)
1405
raise DataError("native command '%s': shell metacharacter '%s' in command line" % (cline, badchar), self.loc)
1407
raise DataError("native command '%s': no method name specified" % cline, self.loc)
1409
self.pycommandpath = re.split('[%s\s]+' % os.pathsep,
1412
self.pycommandpath = None
1413
self.module = parts[0]
1414
self.method = parts[1]
1415
self.cline_list = parts[2:]
1417
def __call__(self, cb):
1419
process.call_native(self.module, self.method, self.cline_list,
1420
loc=self.loc, cb=self._cb, context=self.context,
1421
pycommandpath=self.pycommandpath, **self.kwargs)
1423
def getcommandsforrule(rule, target, makefile, prerequisites, stem):
1424
v = Variables(parent=target.variables)
1425
setautomaticvariables(v, makefile, target, prerequisites)
1426
if stem is not None:
1427
setautomatic(v, '*', [stem])
1429
env = makefile.getsubenvironment(v)
1431
for c in rule.commands:
1432
cstring = c.resolvestr(makefile, v)
1433
for cline in splitcommand(cstring):
1434
cline, isHidden, isRecursive, ignoreErrors, isNative = findmodifiers(cline)
1435
if (isHidden or makefile.silent) and not makefile.justprint:
1438
echo = "%s$ %s" % (c.loc, cline)
1440
yield _CommandWrapper(cline, ignoreErrors=ignoreErrors, env=env, cwd=makefile.workdir, loc=c.loc, context=makefile.context,
1441
echo=echo, justprint=makefile.justprint)
1443
f, s, e = v.get("PYCOMMANDPATH", True)
1445
e = e.resolvestr(makefile, v, ["PYCOMMANDPATH"])
1446
yield _NativeWrapper(cline, ignoreErrors=ignoreErrors,
1447
env=env, cwd=makefile.workdir,
1448
loc=c.loc, context=makefile.context,
1449
echo=echo, justprint=makefile.justprint,
1454
A rule contains a list of prerequisites and a list of commands. It may also
1455
contain rule-specific variables. This rule may be associated with multiple targets.
1458
def __init__(self, prereqs, doublecolon, loc, weakdeps):
1459
self.prerequisites = prereqs
1460
self.doublecolon = doublecolon
1463
self.weakdeps = weakdeps
1465
def addcommand(self, c):
1466
assert isinstance(c, (Expansion, StringExpansion))
1467
self.commands.append(c)
1469
def getcommands(self, target, makefile):
1470
assert isinstance(target, Target)
1472
return getcommandsforrule(self, target, makefile, self.prerequisites, stem=None)
1473
# TODO: $* in non-pattern rules?
1475
class PatternRuleInstance(object):
1479
A pattern rule instantiated for a particular target. It has the same API as Rule, but
1480
different internals, forwarding most information on to the PatternRule.
1482
def __init__(self, prule, dir, stem, ismatchany):
1483
assert isinstance(prule, PatternRule)
1488
self.prerequisites = prule.prerequisitesforstem(dir, stem)
1489
self.doublecolon = prule.doublecolon
1490
self.loc = prule.loc
1491
self.ismatchany = ismatchany
1492
self.commands = prule.commands
1494
def getcommands(self, target, makefile):
1495
assert isinstance(target, Target)
1496
return getcommandsforrule(self, target, makefile, self.prerequisites, stem=self.dir + self.stem)
1499
return "Pattern rule at %s with stem '%s', matchany: %s doublecolon: %s" % (self.loc,
1500
self.dir + self.stem,
1504
class PatternRule(object):
1506
An implicit rule or static pattern rule containing target patterns, prerequisite patterns,
1507
and a list of commands.
1510
def __init__(self, targetpatterns, prerequisites, doublecolon, loc):
1511
self.targetpatterns = targetpatterns
1512
self.prerequisites = prerequisites
1513
self.doublecolon = doublecolon
1517
def addcommand(self, c):
1518
assert isinstance(c, (Expansion, StringExpansion))
1519
self.commands.append(c)
1521
def ismatchany(self):
1522
return util.any((t.ismatchany() for t in self.targetpatterns))
1524
def hasspecificmatch(self, file):
1525
for p in self.targetpatterns:
1526
if not p.ismatchany() and p.match(file) is not None:
1531
def matchesfor(self, dir, file, skipsinglecolonmatchany):
1533
Determine all the target patterns of this rule that might match target t.
1534
@yields a PatternRuleInstance for each.
1537
for p in self.targetpatterns:
1538
matchany = p.ismatchany()
1540
if skipsinglecolonmatchany and not self.doublecolon:
1543
yield PatternRuleInstance(self, dir, file, True)
1545
stem = p.match(dir + file)
1546
if stem is not None:
1547
yield PatternRuleInstance(self, '', stem, False)
1549
stem = p.match(file)
1550
if stem is not None:
1551
yield PatternRuleInstance(self, dir, stem, False)
1553
def prerequisitesforstem(self, dir, stem):
1554
return [p.resolve(dir, stem) for p in self.prerequisites]
1556
class _RemakeContext(object):
1557
def __init__(self, makefile, cb):
1558
self.makefile = makefile
1559
self.included = [(makefile.gettarget(f), required)
1560
for f, required in makefile.included]
1561
self.toremake = list(self.included)
1564
self.remakecb(error=False, didanything=False)
1566
def remakecb(self, error, didanything):
1567
assert error in (True, False)
1569
if error and self.required:
1570
print "Error remaking makefiles (ignored)"
1572
if len(self.toremake):
1573
target, self.required = self.toremake.pop(0)
1574
target.make(self.makefile, [], avoidremakeloop=True, cb=self.remakecb, printerror=False)
1576
for t, required in self.included:
1578
_log.info("Included file %s was remade, restarting make", t.target)
1579
self.cb(remade=True)
1581
elif required and t.mtime is None:
1582
self.cb(remade=False, error=DataError("No rule to remake missing include file %s" % t.target))
1585
self.cb(remade=False)
1587
class Makefile(object):
1589
The top-level data structure for makefile execution. It holds Targets, implicit rules, and other
1593
def __init__(self, workdir=None, env=None, restarts=0, make=None,
1594
makeflags='', makeoverrides='',
1595
makelevel=0, context=None, targets=(), keepgoing=False,
1596
silent=False, justprint=False):
1597
self.defaulttarget = None
1603
self.variables = Variables()
1604
self.variables.readfromenvironment(env)
1606
self.context = context
1607
self.exportedvars = {}
1609
self.keepgoing = keepgoing
1610
self.silent = silent
1611
self.justprint = justprint
1612
self._patternvariables = [] # of (pattern, variables)
1613
self.implicitrules = []
1614
self.parsingfinished = False
1616
self._patternvpaths = [] # of (pattern, [dir, ...])
1619
workdir = os.getcwd()
1620
workdir = os.path.realpath(workdir)
1621
self.workdir = workdir
1622
self.variables.set('CURDIR', Variables.FLAVOR_SIMPLE,
1623
Variables.SOURCE_AUTOMATIC, workdir.replace('\\','/'))
1625
# the list of included makefiles, whether or not they existed
1628
self.variables.set('MAKE_RESTARTS', Variables.FLAVOR_SIMPLE,
1629
Variables.SOURCE_AUTOMATIC, restarts > 0 and str(restarts) or '')
1631
self.variables.set('.PYMAKE', Variables.FLAVOR_SIMPLE,
1632
Variables.SOURCE_MAKEFILE, "1")
1633
if make is not None:
1634
self.variables.set('MAKE', Variables.FLAVOR_SIMPLE,
1635
Variables.SOURCE_MAKEFILE, make)
1637
if makeoverrides != '':
1638
self.variables.set('-*-command-variables-*-', Variables.FLAVOR_SIMPLE,
1639
Variables.SOURCE_AUTOMATIC, makeoverrides)
1640
makeflags += ' -- $(MAKEOVERRIDES)'
1642
self.variables.set('MAKEOVERRIDES', Variables.FLAVOR_RECURSIVE,
1643
Variables.SOURCE_ENVIRONMENT,
1644
'${-*-command-variables-*-}')
1646
self.variables.set('MAKEFLAGS', Variables.FLAVOR_RECURSIVE,
1647
Variables.SOURCE_MAKEFILE, makeflags)
1648
self.exportedvars['MAKEFLAGS'] = True
1650
self.makelevel = makelevel
1651
self.variables.set('MAKELEVEL', Variables.FLAVOR_SIMPLE,
1652
Variables.SOURCE_MAKEFILE, str(makelevel))
1654
self.variables.set('MAKECMDGOALS', Variables.FLAVOR_SIMPLE,
1655
Variables.SOURCE_AUTOMATIC, ' '.join(targets))
1657
for vname, val in implicit.variables.iteritems():
1658
self.variables.set(vname,
1659
Variables.FLAVOR_SIMPLE,
1660
Variables.SOURCE_IMPLICIT, val)
1662
def foundtarget(self, t):
1664
Inform the makefile of a target which is a candidate for being the default target,
1665
if there isn't already a default target.
1667
if self.defaulttarget is None and t != '.PHONY':
1668
self.defaulttarget = t
1670
def getpatternvariables(self, pattern):
1671
assert isinstance(pattern, Pattern)
1673
for p, v in self._patternvariables:
1678
self._patternvariables.append( (pattern, v) )
1681
def getpatternvariablesfor(self, target):
1682
for p, v in self._patternvariables:
1686
def hastarget(self, target):
1687
return target in self._targets
1689
def gettarget(self, target):
1690
assert isinstance(target, str)
1692
target = target.rstrip('/')
1694
assert target != '', "empty target?"
1696
if target.find('*') != -1 or target.find('?') != -1 or target.find('[') != -1:
1697
raise DataError("wildcards should have been expanded by the parser: '%s'" % (target,))
1699
t = self._targets.get(target, None)
1701
t = Target(target, self)
1702
self._targets[target] = t
1705
def appendimplicitrule(self, rule):
1706
assert isinstance(rule, PatternRule)
1707
self.implicitrules.append(rule)
1709
def finishparsing(self):
1711
Various activities, such as "eval", are not allowed after parsing is
1712
finished. In addition, various warnings and errors can only be issued
1713
after the parsing data model is complete. All dependency resolution
1714
and rule execution requires that parsing be finished.
1716
self.parsingfinished = True
1718
flavor, source, value = self.variables.get('GPATH')
1719
if value is not None and value.resolvestr(self, self.variables, ['GPATH']).strip() != '':
1720
raise DataError('GPATH was set: pymake does not support GPATH semantics')
1722
flavor, source, value = self.variables.get('VPATH')
1726
self._vpath = filter(lambda e: e != '',
1727
re.split('[%s\s]+' % os.pathsep,
1728
value.resolvestr(self, self.variables, ['VPATH'])))
1730
targets = list(self._targets.itervalues())
1734
for p in r.prerequisites:
1735
self.gettarget(p).explicit = True
1737
np = self.gettarget('.NOTPARALLEL')
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()
1747
def include(self, path, required=True, weak=False, loc=None):
1749
Include the makefile at `path`.
1751
self.included.append((path, required))
1752
fspath = util.normaljoin(self.workdir, path)
1753
if os.path.exists(fspath):
1754
stmts = parser.parsefile(fspath)
1755
self.variables.append('MAKEFILE_LIST', Variables.SOURCE_AUTOMATIC, path, None, self)
1756
stmts.execute(self, weak=weak)
1757
self.gettarget(path).explicit = True
1759
def addvpath(self, pattern, dirs):
1761
Add a directory to the vpath search for the given pattern.
1763
self._patternvpaths.append((pattern, dirs))
1765
def clearvpath(self, pattern):
1767
Clear vpaths for the given pattern.
1769
self._patternvpaths = [(p, dirs)
1770
for p, dirs in self._patternvpaths
1771
if not p.match(pattern)]
1773
def clearallvpaths(self):
1774
self._patternvpaths = []
1776
def getvpath(self, target):
1777
vp = list(self._vpath)
1778
for p, dirs in self._patternvpaths:
1782
return withoutdups(vp)
1784
def remakemakefiles(self, cb):
1786
for f, required in self.included:
1787
t = self.gettarget(f)
1789
t.resolvevpath(self)
1792
mlist.append((t, oldmtime))
1794
_RemakeContext(self, cb)
1796
def getsubenvironment(self, variables):
1797
env = dict(self.env)
1798
for vname, v in self.exportedvars.iteritems():
1800
flavor, source, val = variables.get(vname)
1804
strval = val.resolvestr(self, variables, [vname])
1807
env.pop(vname, None)
1811
env['MAKELEVEL'] = str(self.makelevel + 1)