123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115 |
- from io import open
- from os.path import isdir
- import re
- def filterLines(lines, regex):
- '''Filters each line of the given line iterator using the given regular
- expression string. For each match, a tuple containing the text matching
- each capture group from the regular expression is yielded.
- '''
- matcher = re.compile(regex)
- for line in lines:
- if line.endswith('\n'):
- line = line[ : -1]
- match = matcher.match(line)
- if match:
- yield match.groups()
- def filterFile(filePath, regex):
- '''Filters each line of the given text file using the given regular
- expression string. For each match, a tuple containing the text matching
- each capture group from the regular expression is yielded.
- '''
- with open(filePath, 'r', encoding='utf-8') as inp:
- for groups in filterLines(inp, regex):
- yield groups
- def joinContinuedLines(lines):
- '''Iterates through the given lines, replacing lines that are continued
- using a trailing backslash with a single line.
- '''
- buf = ''
- for line in lines:
- if line.endswith('\\\n'):
- buf += line[ : -2]
- elif line.endswith('\\'):
- buf += line[ : -1]
- else:
- yield buf + line
- buf = ''
- if buf:
- raise ValueError('Continuation on last line')
- _reEval = re.compile('(\$\(|\))')
- def evalMakeExpr(expr, makeVars):
- '''Evaluates variable references in an expression.
- Raises ValueError if there is a syntax error in the expression.
- Raises KeyError if the expression references a non-existing variable.
- '''
- stack = [ [] ]
- for part in _reEval.split(expr):
- if part == '$(':
- stack.append([])
- elif part == ')' and len(stack) != 1:
- name = ''.join(stack.pop())
- if name.startswith('addprefix '):
- prefix, args = name[len('addprefix') : ].split(',')
- prefix = prefix.strip()
- value = ' '.join(prefix + arg for arg in args.split())
- elif name.startswith('addsuffix '):
- suffix, args = name[len('addsuffix') : ].split(',')
- suffix = suffix.strip()
- value = ' '.join(arg + suffix for arg in args.split())
- elif name.startswith('shell '):
- # Unsupported; assume result is never used.
- value = '?'
- elif name.isdigit():
- # This is a function argument; assume the evaluated result is
- # never used.
- value = '?'
- else:
- value = makeVars[name]
- stack[-1].append(value)
- else:
- stack[-1].append(part)
- if len(stack) != 1:
- raise ValueError('Open without close in "%s"' % expr)
- return ''.join(stack.pop())
- def extractMakeVariables(filePath, makeVars = None):
- '''Extract all variable definitions from the given Makefile.
- The optional makeVars argument is a dictionary containing the already
- defined variables. These variables will be included in the output; the
- given dictionary is not modified.
- Returns a dictionary that maps each variable name to its value.
- '''
- makeVars = {} if makeVars is None else dict(makeVars)
- with open(filePath, 'r', encoding='utf-8') as inp:
- for name, assign, value in filterLines(
- joinContinuedLines(inp),
- r'[ ]*([A-Za-z0-9_]+)[ ]*([+:]?=)(.*)'
- ):
- if assign == '=':
- makeVars[name] = value.strip()
- elif assign == ':=':
- makeVars[name] = evalMakeExpr(value, makeVars).strip()
- elif assign == '+=':
- # Note: Make will or will not evaluate the added expression
- # depending on how the variable was originally defined,
- # but we don't store that information.
- makeVars[name] = makeVars[name] + ' ' + value.strip()
- else:
- assert False, assign
- return makeVars
- def parseBool(valueStr):
- '''Parses a string containing a boolean value.
- Accepted values are "true" and "false"; anything else raises ValueError.
- '''
- if valueStr == 'true':
- return True
- elif valueStr == 'false':
- return False
- else:
- raise ValueError('Invalid boolean "%s"' % valueStr)
|