def expand_macros(raw_text, macros):
includes = {}<EOL>result = []<EOL>pattern = re.compile("<STR_LIT>", re.UNICODE)<EOL>ipattern = re.compile("<STR_LIT>", re.UNICODE)<EOL>for line in raw_text.split("<STR_LIT:\n>"):<EOL><INDENT>line = string.Template(line).safe_substitute(macros)<EOL>result.append(line)<EOL>if line.startswith("<STR_LIT>"):<EOL><INDENT>match = pattern.match(line)<EOL>try:<EOL><INDENT>var, opt, val, or_ = match.group(<NUM_LIT:1>, <NUM_LIT:2>, <NUM_LIT:3>, <NUM_LIT:4>)<EOL><DEDENT>except:<EOL><INDENT>raise InvalidMacroError("<STR_LIT>".format(line))<EOL><DEDENT>if or_:<EOL><INDENT>if var not in macros:<EOL><INDENT>raise InvalidMacroError("<STR_LIT>".format(var, or_))<EOL><DEDENT><DEDENT>elif not (opt and var in macros):<EOL><INDENT>macros[var] = val<EOL><DEDENT><DEDENT>elif line.startswith("<STR_LIT>"):<EOL><INDENT>match = ipattern.match(line)<EOL>try:<EOL><INDENT>filename = match.group(<NUM_LIT:1>)<EOL><DEDENT>except:<EOL><INDENT>error("<STR_LIT>".format(line))<EOL>sys.exit(<NUM_LIT:1>)<EOL><DEDENT>try:<EOL><INDENT>with io.open(filename, '<STR_LIT:r>') as f:<EOL><INDENT>includes[filename] = expand_macros(f.read(), macros)<EOL><DEDENT><DEDENT>except IOError:<EOL><INDENT>if match.group(<NUM_LIT:2>):<EOL><INDENT>if match.group(<NUM_LIT:2>).startswith('<STR_LIT>'):<EOL><INDENT>sprint(match.group(<NUM_LIT:3>))<EOL><DEDENT><DEDENT>else:<EOL><INDENT>error("<STR_LIT>".format(filename))<EOL>sys.exit(<NUM_LIT:1>)<EOL><DEDENT><DEDENT><DEDENT><DEDENT>return "<STR_LIT:\n>".join(result), includes<EOL>
this gets called before the sakefile is parsed. it looks for
macros defined anywhere in the sakefile (the start of the line
is '#!') and then replaces all occurences of '$variable' with the
value defined in the macro. it then returns the contents of the
file with the macros expanded.