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