blob: 4275b8a56a3be4aad21a22fddfa914876fd89777 [file] [log] [blame]
import os
import re
import lit.util
expr = re.compile(r"^(\\)?((\| )?)\W+b(\S+)\\b\W*$")
wordifier = re.compile(r"(\W*)(\b[^\b]+\b)")
class FindTool(object):
def __init__(self, name): = name
def resolve(self, config, dirs):
# Check for a user explicitely overriding a tool. This allows:
# llvm-lit -D llc="llc -enable-misched -verify-machineinstrs"
command = config.lit_config.params.get(
if command is None:
# Then check out search paths.
command = lit.util.which(, dirs)
if not command:
return None
if == 'llc' and os.environ.get('LLVM_ENABLE_MACHINE_VERIFIER') == '1':
command += ' -verify-machineinstrs'
elif == 'llvm-go':
exe = getattr(config.config, 'go_executable', None)
if exe:
command += ' go=' + exe
return command
class ToolSubst(object):
"""String-like class used to build regex substitution patterns for llvm
Handles things like adding word-boundary patterns, and filtering
characters from the beginning an end of a tool name
def __init__(self, key, command=None, pre=r'.-^/\<', post='-.', verbatim=False,
unresolved='warn', extra_args=None):
"""Construct a ToolSubst.
key: The text which is to be substituted.
command: The command to substitute when the key is matched. By default,
this will treat `key` as a tool name and search for it. If it is
a string, it is intereprted as an exact path. If it is an instance of
FindTool, the specified tool name is searched for on disk.
pre: If specified, the substitution will not find matches where
the character immediately preceding the word-boundary that begins
`key` is any of the characters in the string `pre`.
post: If specified, the substitution will not find matches where
the character immediately after the word-boundary that ends `key`
is any of the characters specified in the string `post`.
verbatim: If True, `key` is an exact regex that is passed to the
underlying substitution
unresolved: Action to take if the tool substitution cannot be
resolved. Valid values:
'warn' - log a warning but add the substitution anyway.
'fatal' - Exit the test suite and log a fatal error.
'break' - Don't add any of the substitutions from the current
group, and return a value indicating a failure.
'ignore' - Don't add the substitution, and don't log an error
extra_args: If specified, represents a list of arguments that will be
appended to the tool's substitution.
explicit_path: If specified, the exact path will be used as a substitution.
Otherwise, the tool will be searched for as if by calling which(tool)
self.unresolved = unresolved
self.extra_args = extra_args
self.key = key
self.command = command if command is not None else FindTool(key)
self.was_resolved = False
if verbatim:
self.regex = key
def not_in(chars, where=''):
if not chars:
return ''
pattern_str = '|'.join(re.escape(x) for x in chars)
return r'(?{}!({}))'.format(where, pattern_str)
def wordify(word):
match = wordifier.match(word)
introducer =
word =
return introducer + r'\b' + word + r'\b'
self.regex = not_in(pre, '<') + wordify(key) + not_in(post)
def resolve(self, config, search_dirs):
# Extract the tool name from the pattern. This relies on the tool
# name being surrounded by \b word match operators. If the
# pattern starts with "| ", include it in the string to be
# substituted.
tool_match = expr.match(self.regex)
if not tool_match:
return None
tool_pipe =
tool_name =
if isinstance(self.command, FindTool):
command_str = self.command.resolve(config, search_dirs)
command_str = str(self.command)
if command_str:
if self.extra_args:
command_str = ' '.join([command_str] + self.extra_args)
if self.unresolved == 'warn':
# Warn, but still provide a substitution.
'Did not find ' + tool_name + ' in %s' % search_dirs)
command_str = os.path.join(
config.config.llvm_tools_dir, tool_name)
elif self.unresolved == 'fatal':
# The function won't even return in this case, this leads to
# sys.exit
'Did not find ' + tool_name + ' in %s' % search_dirs)
elif self.unresolved == 'break':
# By returning a valid result with an empty command, the
# caller treats this as a failure.
elif self.unresolved == 'ignore':
# By returning None, the caller just assumes there was no
# match in the first place.
return None
raise 'Unexpected value for ToolSubst.unresolved'
if command_str:
self.was_resolved = True
return (self.regex, tool_pipe, command_str)