def parsedepfile(pathname): """ Parse a filename listing only depencencies into a parserdata.StatementList. Simple variable references are allowed in such files. """ def continuation_iter(lines): current_line = [] for line in lines: line = line.rstrip() if line.endswith("\\"): current_line.append(line.rstrip("\\")) continue if not len(line): continue current_line.append(line) yield ''.join(current_line) current_line = [] if current_line: yield ''.join(current_line) def get_expansion(s): if '$' in s: expansion = data.Expansion() # for an input like e.g. "foo $(bar) baz", # _vars.split returns ["foo", "bar", "baz"] # every other element is a variable name. for i, element in enumerate(_vars.split(s)): if i % 2: expansion.appendfunc( functions.VariableRef( None, data.StringExpansion(element, None))) elif element: expansion.appendstr(element) return expansion return data.StringExpansion(s, None) pathname = os.path.realpath(pathname) stmts = parserdata.StatementList() for line in continuation_iter(open(pathname).readlines()): target, deps = _depfilesplitter.split(line, 1) stmts.append( parserdata.Rule(get_expansion(target), get_expansion(deps), False)) return stmts
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 errors.SyntaxError("unmatched 'endif' directive", d.getloc(offset)) condstack.pop().endloc = d.getloc(offset) continue if kword == 'else': if len(condstack) == 1: raise errors.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 errors.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 errors.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 errors.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 errors.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 errors.SyntaxError("Condition never terminated with endif", condstack[-1].loc) return condstack[0]