Index: clang/test/lit.cfg.py =================================================================== --- clang/test/lit.cfg.py +++ clang/test/lit.cfg.py @@ -10,7 +10,7 @@ import lit.util from lit.llvm import llvm_config -from lit.llvm import ToolFilter +from lit.llvm.subst import ToolSubst # Configuration file for the 'lit' test runner. @@ -72,6 +72,8 @@ # Propagate path to symbolizer for ASan/MSan. llvm_config.with_system_environment(['ASAN_SYMBOLIZER_PATH', 'MSAN_SYMBOLIZER_PATH']) +llvm_config.use_default_substitutions() + # Discover the 'clang' and 'clangcc' to use. def inferClang(PATH): @@ -181,18 +183,15 @@ tool_dirs = [config.clang_tools_dir, config.llvm_tools_dir] tool_patterns = [ - 'FileCheck', 'c-index-test', - ToolFilter('clang-check', pre='-.', post='-.'), - ToolFilter('clang-diff', pre='-.', post='-.'), - ToolFilter('clang-format', pre='-.', post='-.'), + 'c-index-test', + ToolSubst('clang-check', pre='-.', post='-.'), + ToolSubst('clang-diff', pre='-.', post='-.'), + ToolSubst('clang-format', pre='-.', post='-.'), # FIXME: Some clang test uses opt? - ToolFilter('opt', pre='-.', post=r'/\-.'), - # Handle these specially as they are strings searched for during testing. - ToolFilter(r'\| \bcount\b', verbatim=True), - ToolFilter(r'\| \bnot\b', verbatim=True)] + ToolSubst('opt', pre='-.', post=r'/\-.')] if config.clang_examples: - tool_patterns.append(ToolFilter('clang-interpreter', '-.', '-.')) + tool_patterns.append(ToolSubst('clang-interpreter', '-.', '-.')) llvm_config.add_tool_substitutions(tool_patterns, tool_dirs) Index: lld/test/lit.cfg.py =================================================================== --- lld/test/lit.cfg.py +++ lld/test/lit.cfg.py @@ -10,7 +10,7 @@ import lit.util from lit.llvm import llvm_config -from lit.llvm import ToolFilter +from lit.llvm.subst import ToolSubst # Configuration file for the 'lit' test runner. @@ -43,6 +43,8 @@ llvm_config.with_environment('LD_LIBRARY_PATH', [config.lld_libs_dir, config.llvm_libs_dir], append_path=True) +llvm_config.use_default_substitutions() + # For each occurrence of a clang tool name, replace it with the full path to # the build directory holding that tool. We explicitly specify the directories # to search to ensure that we get the tools just built and not some random @@ -52,9 +54,9 @@ config.substitutions.append( (r"\bld.lld\b", 'ld.lld --full-shutdown') ) tool_patterns = [ - 'FileCheck', 'not', 'ld.lld', 'lld-link', 'llvm-as', 'llvm-mc', 'llvm-nm', + 'ld.lld', 'lld-link', 'llvm-as', 'llvm-mc', 'llvm-nm', 'llvm-objdump', 'llvm-pdbutil', 'llvm-readobj', 'obj2yaml', 'yaml2obj', - ToolFilter('lld', pre='-./', post='-.')] + ToolSubst('lld', pre='-./', post='-.')] llvm_config.add_tool_substitutions(tool_patterns, tool_dirs) Index: llvm/test/lit.cfg.py =================================================================== --- llvm/test/lit.cfg.py +++ llvm/test/lit.cfg.py @@ -11,7 +11,7 @@ import lit.util import lit.formats from lit.llvm import llvm_config -from lit.llvm import ToolFilter +from lit.llvm.subst import ToolSubst # name: The name of this test suite. config.name = 'LLVM' @@ -137,7 +137,9 @@ # also have a post-assertion to not match a trailing hyphen (foo-). JUNKCHARS = r".-^/<" -required_tools = [ +llvm_config.use_default_substitutions() + +tools = [ 'lli', 'llvm-ar', 'llvm-as', 'llvm-bcanalyzer', 'llvm-config', 'llvm-cov', 'llvm-cxxdump', 'llvm-cvtres', 'llvm-diff', 'llvm-dis', 'llvm-dsymutil', 'llvm-dwarfdump', 'llvm-extract', 'llvm-isel-fuzzer', 'llvm-lib', @@ -146,28 +148,21 @@ 'llvm-pdbutil', 'llvm-profdata', 'llvm-ranlib', 'llvm-readobj', 'llvm-rtdyld', 'llvm-size', 'llvm-split', 'llvm-strings', 'llvm-tblgen', 'llvm-c-test', 'llvm-cxxfilt', 'llvm-xray', 'yaml2obj', 'obj2yaml', - 'FileCheck', 'yaml-bench', 'verify-uselistorder', - ToolFilter('bugpoint', post='-'), - ToolFilter('llc', pre=JUNKCHARS), - ToolFilter('llvm-symbolizer', pre=JUNKCHARS), - ToolFilter('opt', JUNKCHARS), - ToolFilter('sancov', pre=JUNKCHARS), - ToolFilter('sanstats', pre=JUNKCHARS), - # Handle these specially as they are strings searched for during testing. - ToolFilter(r'\| \bcount\b', verbatim=True), - ToolFilter(r'\| \bnot\b', verbatim=True)] - -llvm_config.add_tool_substitutions(required_tools, config.llvm_tools_dir) - -# For tools that are optional depending on the config, we won't warn -# if they're missing. - -optional_tools = [ + 'yaml-bench', 'verify-uselistorder', + ToolSubst('bugpoint', post='-', ), + ToolSubst('llc', pre=JUNKCHARS), + ToolSubst('llvm-symbolizer', pre=JUNKCHARS), + ToolSubst('opt', JUNKCHARS), + ToolSubst('sancov', pre=JUNKCHARS), + ToolSubst('sanstats', pre=JUNKCHARS)] + +# The following tools are optional +tools.extend(map(lambda x : ToolSubst(x, unresolved='ignore'), [ 'llvm-go', 'llvm-mt', 'Kaleidoscope-Ch3', 'Kaleidoscope-Ch4', 'Kaleidoscope-Ch5', 'Kaleidoscope-Ch6', 'Kaleidoscope-Ch7', - 'Kaleidoscope-Ch8'] -llvm_config.add_tool_substitutions(optional_tools, config.llvm_tools_dir, - warn_missing=False) + 'Kaleidoscope-Ch8'])) + +llvm_config.add_tool_substitutions(tools, config.llvm_tools_dir) ### Targets Index: llvm/utils/lit/lit/llvm/__init__.py =================================================================== --- llvm/utils/lit/lit/llvm/__init__.py +++ llvm/utils/lit/lit/llvm/__init__.py @@ -1,49 +1,7 @@ from lit.llvm import config -import lit.util -import re llvm_config = None -class ToolFilter(object): - """ - String-like class used to build regex substitution patterns for - llvm tools. Handles things like adding word-boundary patterns, - and filtering characters from the beginning an end of a tool name - """ - - def __init__(self, name, pre=None, post=None, verbatim=False): - """ - Construct a ToolFilter. - - name: the literal name of the substitution to look for. - - pre: If specified, the substitution will not find matches where - the character immediately preceding the word-boundary that begins - `name` 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 `name` - is any of the characters specified in the string `post`. - - verbatim: If True, `name` is an exact regex that is passed to the - underlying substitution - """ - if verbatim: - self.regex = name - return - - 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) - - self.regex = not_in(pre, '<') + r'\b' + name + r'\b' + not_in(post) - - def __str__(self): - return self.regex - - def initialize(lit_config, test_config): global llvm_config Index: llvm/utils/lit/lit/llvm/config.py =================================================================== --- llvm/utils/lit/lit/llvm/config.py +++ llvm/utils/lit/lit/llvm/config.py @@ -5,6 +5,7 @@ import sys import lit.util +from lit.llvm.subst import ToolSubst def binary_feature(on, feature, off_prefix): return feature if on else off_prefix + feature @@ -219,23 +220,26 @@ # -win32 is not supported for non-x86 targets; use a default. return 'i686-pc-win32' - def add_tool_substitutions(self, tools, search_dirs, warn_missing = True): + def add_tool_substitutions(self, tools, search_dirs = None): + if not search_dirs: + search_dirs = [self.config.llvm_tools_dir] + if lit.util.is_string(search_dirs): search_dirs = [search_dirs] + tools = [x if isinstance(x, ToolSubst) else ToolSubst(x) for x in tools] + search_dirs = os.pathsep.join(search_dirs) + substitutions = [] for tool in tools: # 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. - if lit.util.is_string(tool): - tool = lit.util.make_word_regex(tool) - else: - tool = str(tool) + tool_str = str(tool) tool_match = re.match(r"^(\\)?((\| )?)\W+b([0-9A-Za-z-_\.]+)\\b\W*$", - tool) + tool_str) if not tool_match: continue @@ -243,10 +247,18 @@ tool_name = tool_match.group(4) tool_path = lit.util.which(tool_name, search_dirs) if not tool_path: - if warn_missing: + if tool.unresolved == 'warn': # Warn, but still provide a substitution. self.lit_config.note('Did not find ' + tool_name + ' in %s' % search_dirs) - tool_path = self.config.llvm_tools_dir + '/' + tool_name + tool_path = self.config.llvm_tools_dir + '/' + tool_name + elif tool.unresolved == 'fatal': + self.lit_config.fatal('Did not find ' + tool_name + ' in %s' % search_dirs) + elif tool.unresolved == 'break': + return False + elif tool.unresolved == 'ignore': + continue + else: + raise "Unexpected value for ToolSubst.unresolved" if tool_name == 'llc' and os.environ.get('LLVM_ENABLE_MACHINE_VERIFIER') == '1': tool_path += ' -verify-machineinstrs' @@ -255,4 +267,18 @@ if exe: tool_path += " go=" + exe - self.config.substitutions.append((tool, tool_pipe + tool_path)) + pattern = tool.introducer + tool_str + substitutions.append((pattern, tool_pipe + tool_path)) + + self.config.substitutions.extend(substitutions) + return True + + def use_default_substitutions(self): + tool_patterns = [ + ToolSubst('FileCheck', unresolved='fatal'), + # Handle these specially as they are strings searched for during testing. + ToolSubst(r'\| \bcount\b', verbatim=True, unresolved='fatal'), + ToolSubst(r'\| \bnot\b', verbatim=True, unresolved='fatal')] + + self.config.substitutions.append( ('%python', sys.executable) ) + self.add_tool_substitutions(tool_patterns, [self.config.llvm_tools_dir]) Index: llvm/utils/lit/lit/llvm/subst.py =================================================================== --- /dev/null +++ llvm/utils/lit/lit/llvm/subst.py @@ -0,0 +1,57 @@ +import re + +class ToolSubst(object): + """ + String-like class used to build regex substitution patterns for + llvm tools. Handles things like adding word-boundary patterns, + and filtering characters from the beginning an end of a tool name + """ + + def __init__(self, name, pre=None, post=None, verbatim=False, + unresolved='warn', introducer=''): + """ + Construct a ToolSubst. + + name: the literal name of the substitution to look for. + + pre: If specified, the substitution will not find matches where + the character immediately preceding the word-boundary that begins + `name` 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 `name` + is any of the characters specified in the string `post`. + + verbatim: If True, `name` 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 + + introducer: If specified, prepends to the beginning of the tool name + pattern. For example, specifying an introducer of '%' with a tool + name of 'clang' would cause %clang to be substituted for the specified + path. + + """ + self.unresolved = unresolved + self.introducer = introducer + if verbatim: + self.regex = name + return + + 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) + + self.regex = not_in(pre, '<') + r'\b' + name + r'\b' + not_in(post) + + def __str__(self): + return self.regex