def parsestring(s, filename): """ Parse a string containing makefile data into a parserdata.StatementList. """ currule = False condstack = [parserdata.StatementList()] fdlines = enumeratelines(s, filename) for d in fdlines: assert len(condstack) > 0 offset = d.lstart if currule and offset < d.lend and d.s[offset] == '\t': e, token, offset = parsemakesyntax(d, offset + 1, (), itercommandchars) assert token is None assert offset is None condstack[-1].append(parserdata.Command(e)) continue # To parse Makefile syntax, we first strip leading whitespace and # look for initial keywords. If there are no keywords, it's either # setting a variable or writing a rule. offset = d.skipwhitespace(offset) if offset is None: continue m = _directivesre.match(d.s, offset, d.lend) if m is not None: kword = m.group(1) offset = m.end(0) if kword == 'endif': _ensureend(d, offset, "Unexpected data after 'endif' directive") if len(condstack) == 1: raise SyntaxError("unmatched 'endif' directive", d.getloc(offset)) condstack.pop().endloc = d.getloc(offset) continue if kword == 'else': if len(condstack) == 1: raise SyntaxError("unmatched 'else' directive", d.getloc(offset)) m = _conditionre.match(d.s, offset, d.lend) if m is None: _ensureend(d, offset, "Unexpected data after 'else' directive.") condstack[-1].addcondition(d.getloc(offset), parserdata.ElseCondition()) else: kword = m.group(1) if kword not in _conditionkeywords: raise SyntaxError( "Unexpected condition after 'else' directive.", d.getloc(offset)) startoffset = offset offset = d.skipwhitespace(m.end(1)) c = _conditionkeywords[kword](d, offset) condstack[-1].addcondition(d.getloc(startoffset), c) continue if kword in _conditionkeywords: c = _conditionkeywords[kword](d, offset) cb = parserdata.ConditionBlock(d.getloc(d.lstart), c) condstack[-1].append(cb) condstack.append(cb) continue if kword == 'endef': raise SyntaxError("endef without matching define", d.getloc(offset)) if kword == 'define': currule = False vname, t, i = parsemakesyntax(d, offset, (), itermakefilechars) vname.rstrip() startloc = d.getloc(d.lstart) value = iterdefinelines(fdlines, startloc) condstack[-1].append( parserdata.SetVariable(vname, value=value, valueloc=startloc, token='=', targetexp=None)) continue if kword in ('include', '-include', 'includedeps', '-includedeps'): if kword.startswith('-'): required = False kword = kword[1:] else: required = True deps = kword == 'includedeps' currule = False incfile, t, offset = parsemakesyntax(d, offset, (), itermakefilechars) condstack[-1].append( parserdata.Include(incfile, required, deps)) continue if kword == 'vpath': currule = False e, t, offset = parsemakesyntax(d, offset, (), itermakefilechars) condstack[-1].append(parserdata.VPathDirective(e)) continue if kword == 'override': currule = False vname, token, offset = parsemakesyntax(d, offset, _varsettokens, itermakefilechars) vname.lstrip() vname.rstrip() if token is None: raise SyntaxError("Malformed override directive, need =", d.getloc(d.lstart)) value = flattenmakesyntax(d, offset).lstrip() condstack[-1].append( parserdata.SetVariable( vname, value=value, valueloc=d.getloc(offset), token=token, targetexp=None, source=data.Variables.SOURCE_OVERRIDE)) continue if kword == 'export': currule = False e, token, offset = parsemakesyntax(d, offset, _varsettokens, itermakefilechars) e.lstrip() e.rstrip() if token is None: condstack[-1].append( parserdata.ExportDirective(e, concurrent_set=False)) else: condstack[-1].append( parserdata.ExportDirective(e, concurrent_set=True)) value = flattenmakesyntax(d, offset).lstrip() condstack[-1].append( parserdata.SetVariable(e, value=value, valueloc=d.getloc(offset), token=token, targetexp=None)) continue if kword == 'unexport': e, token, offset = parsemakesyntax(d, offset, (), itermakefilechars) condstack[-1].append(parserdata.UnexportDirective(e)) continue e, token, offset = parsemakesyntax(d, offset, _varsettokens + ('::', ':'), itermakefilechars) if token is None: e.rstrip() e.lstrip() if not e.isempty(): condstack[-1].append(parserdata.EmptyDirective(e)) continue # if we encountered real makefile syntax, the current rule is over currule = False if token in _varsettokens: e.lstrip() e.rstrip() value = flattenmakesyntax(d, offset).lstrip() condstack[-1].append( parserdata.SetVariable(e, value=value, valueloc=d.getloc(offset), token=token, targetexp=None)) else: doublecolon = token == '::' # `e` is targets or target patterns, which can end up as # * a rule # * an implicit rule # * a static pattern rule # * a target-specific variable definition # * a pattern-specific variable definition # any of the rules may have order-only prerequisites # delimited by |, and a command delimited by ; targets = e e, token, offset = parsemakesyntax(d, offset, _varsettokens + (':', '|', ';'), itermakefilechars) if token in (None, ';'): condstack[-1].append(parserdata.Rule(targets, e, doublecolon)) currule = True if token == ';': offset = d.skipwhitespace(offset) e, t, offset = parsemakesyntax(d, offset, (), itercommandchars) condstack[-1].append(parserdata.Command(e)) elif token in _varsettokens: e.lstrip() e.rstrip() value = flattenmakesyntax(d, offset).lstrip() condstack[-1].append( parserdata.SetVariable(e, value=value, valueloc=d.getloc(offset), token=token, targetexp=targets)) elif token == '|': raise SyntaxError('order-only prerequisites not implemented', d.getloc(offset)) else: assert token == ':' # static pattern rule pattern = e deps, token, offset = parsemakesyntax(d, offset, (';', ), itermakefilechars) condstack[-1].append( parserdata.StaticPatternRule(targets, pattern, deps, doublecolon)) currule = True if token == ';': offset = d.skipwhitespace(offset) e, token, offset = parsemakesyntax(d, offset, (), itercommandchars) condstack[-1].append(parserdata.Command(e)) if len(condstack) != 1: raise SyntaxError("Condition never terminated with endif", condstack[-1].loc) return condstack[0]
def parsestream(fd, filename): """ Parse a stream of makefile into a parserdata.StatementList. To parse a file system file, use parsefile instead of this method. @param fd A file-like object containing the makefile data. """ currule = False condstack = [parserdata.StatementList()] fdlines = enumerate(fd) while True: assert len(condstack) > 0 d = DynamicData(fdlines, filename) if d.data is None: break if len(d.data) > 0 and d.data[0] == '\t' and currule: e, t, o = parsemakesyntax(d, 1, (), itercommandchars) assert t == None condstack[-1].append(parserdata.Command(e)) else: # To parse Makefile syntax, we first strip leading whitespace and # look for initial keywords. If there are no keywords, it's either # setting a variable or writing a rule. offset = d.skipwhitespace(0) kword, offset = d.findtoken(offset, _directivestokenlist, True) if kword == 'endif': _ensureend(d, offset, "Unexpected data after 'endif' directive") if len(condstack) == 1: raise SyntaxError("unmatched 'endif' directive", d.getloc(offset)) condstack.pop() continue if kword == 'else': if len(condstack) == 1: raise SyntaxError("unmatched 'else' directive", d.getloc(offset)) kword, offset = d.findtoken(offset, _conditionkeywordstokenlist, True) if kword is None: _ensureend(d, offset, "Unexpected data after 'else' directive.") condstack[-1].addcondition(d.getloc(offset), parserdata.ElseCondition()) else: if kword not in _conditionkeywords: raise SyntaxError( "Unexpected condition after 'else' directive.", d.getloc(offset)) c = _conditionkeywords[kword](d, offset) condstack[-1].addcondition(d.getloc(offset), c) continue if kword in _conditionkeywords: c = _conditionkeywords[kword](d, offset) cb = parserdata.ConditionBlock(d.getloc(0), c) condstack[-1].append(cb) condstack.append(cb) continue if kword == 'endef': raise SyntaxError("Unmatched endef", d.getloc(offset)) if kword == 'define': currule = False vname, t, i = parsemakesyntax(d, offset, (), itermakefilechars) vname.rstrip() startpos = len(d.data) if not d.readline(): raise SyntaxError("Unterminated define", d.getloc()) value = _iterflatten(iterdefinechars, d, startpos) condstack[-1].append( parserdata.SetVariable(vname, value=value, valueloc=d.getloc(0), token='=', targetexp=None)) continue if kword in ('include', '-include'): currule = False incfile, t, offset = parsemakesyntax(d, offset, (), itermakefilechars) condstack[-1].append( parserdata.Include(incfile, kword == 'include')) continue if kword == 'vpath': currule = False e, t, offset = parsemakesyntax(d, offset, (), itermakefilechars) condstack[-1].append(parserdata.VPathDirective(e)) continue if kword == 'override': currule = False vname, token, offset = parsemakesyntax(d, offset, _varsettokens, itermakefilechars) vname.lstrip() vname.rstrip() if token is None: raise SyntaxError("Malformed override directive, need =", d.getloc(offset)) value = _iterflatten(itermakefilechars, d, offset).lstrip() condstack[-1].append( parserdata.SetVariable( vname, value=value, valueloc=d.getloc(offset), token=token, targetexp=None, source=data.Variables.SOURCE_OVERRIDE)) continue if kword == 'export': currule = False e, token, offset = parsemakesyntax(d, offset, _varsettokens, itermakefilechars) e.lstrip() e.rstrip() if token is None: condstack[-1].append( parserdata.ExportDirective(e, single=False)) else: condstack[-1].append( parserdata.ExportDirective(e, single=True)) value = _iterflatten(itermakefilechars, d, offset).lstrip() condstack[-1].append( parserdata.SetVariable(e, value=value, valueloc=d.getloc(offset), token=token, targetexp=None)) continue if kword == 'unexport': e, token, offset = parsemakesyntax(d, offset, (), itermakefilechars) condstack[-1].append(parserdata.UnexportDirective(e)) continue assert kword is None, "unexpected kword: %r" % (kword, ) e, token, offset = parsemakesyntax(d, offset, _varsettokens + ('::', ':'), itermakefilechars) if token is None: e.rstrip() e.lstrip() if not e.isempty(): condstack[-1].append(parserdata.EmptyDirective(e)) continue # if we encountered real makefile syntax, the current rule is over currule = False if token in _varsettokens: e.lstrip() e.rstrip() value = _iterflatten(itermakefilechars, d, offset).lstrip() condstack[-1].append( parserdata.SetVariable(e, value=value, valueloc=d.getloc(offset), token=token, targetexp=None)) else: doublecolon = token == '::' # `e` is targets or target patterns, which can end up as # * a rule # * an implicit rule # * a static pattern rule # * a target-specific variable definition # * a pattern-specific variable definition # any of the rules may have order-only prerequisites # delimited by |, and a command delimited by ; targets = e e, token, offset = parsemakesyntax( d, offset, _varsettokens + (':', '|', ';'), itermakefilechars) if token in (None, ';'): condstack[-1].append( parserdata.Rule(targets, e, doublecolon)) currule = True if token == ';': offset = d.skipwhitespace(offset) e, t, offset = parsemakesyntax(d, offset, (), itercommandchars) condstack[-1].append(parserdata.Command(e)) elif token in _varsettokens: e.lstrip() e.rstrip() value = _iterflatten(itermakefilechars, d, offset).lstrip() condstack[-1].append( parserdata.SetVariable(e, value=value, valueloc=d.getloc(offset), token=token, targetexp=targets)) elif token == '|': raise SyntaxError( 'order-only prerequisites not implemented', d.getloc(offset)) else: assert token == ':' # static pattern rule pattern = e deps, token, offset = parsemakesyntax( d, offset, (';', ), itermakefilechars) condstack[-1].append( parserdata.StaticPatternRule(targets, pattern, deps, doublecolon)) currule = True if token == ';': offset = d.skipwhitespace(offset) e, token, offset = parsemakesyntax( d, offset, (), itercommandchars) condstack[-1].append(parserdata.Command(e)) if len(condstack) != 1: raise SyntaxError("Condition never terminated with endif", condstack[-1].loc) return condstack[0]