diff options
Diffstat (limited to 'llvm')
-rw-r--r-- | llvm/test/lit.cfg.py | 119 | ||||
-rw-r--r-- | llvm/utils/lit/lit/llvm/__init__.py | 42 | ||||
-rw-r--r-- | llvm/utils/lit/lit/llvm/config.py | 68 | ||||
-rw-r--r-- | llvm/utils/lit/lit/llvm/subst.py | 140 | ||||
-rw-r--r-- | llvm/utils/lit/lit/util.py | 4 |
5 files changed, 232 insertions, 141 deletions
diff --git a/llvm/test/lit.cfg.py b/llvm/test/lit.cfg.py index 26a394f7878..7b935c19558 100644 --- a/llvm/test/lit.cfg.py +++ b/llvm/test/lit.cfg.py @@ -11,7 +11,8 @@ import subprocess import lit.util import lit.formats from lit.llvm import llvm_config -from lit.llvm import ToolFilter +from lit.llvm.subst import FindTool +from lit.llvm.subst import ToolSubst # name: The name of this test suite. config.name = 'LLVM' @@ -82,31 +83,30 @@ def get_asan_rtlib(): return found_dylibs[0] -lli = 'lli' +llvm_config.use_default_substitutions() + +# Add site-specific substitutions. +config.substitutions.append(('%llvmshlibdir', config.llvm_shlib_dir)) +config.substitutions.append(('%shlibext', config.llvm_shlib_ext)) +config.substitutions.append(('%exeext', config.llvm_exe_ext)) +config.substitutions.append(('%host_cc', config.host_cc)) + + +lli_args = [] # The target triple used by default by lli is the process target triple (some # triple appropriate for generating code for the current process) but because # we don't support COFF in MCJIT well enough for the tests, force ELF format on # Windows. FIXME: the process target triple should be used here, but this is # difficult to obtain on Windows. if re.search(r'cygwin|mingw32|windows-gnu|windows-msvc|win32', config.host_triple): - lli += ' -mtriple=' + config.host_triple + '-elf' -config.substitutions.append(('%lli', lli)) + lli_args = ['-mtriple=' + config.host_triple + '-elf'] + +llc_args = [] # Similarly, have a macro to use llc with DWARF even when the host is win32. -llc_dwarf = 'llc' if re.search(r'win32', config.target_triple): - llc_dwarf += ' -mtriple=' + \ - config.target_triple.replace('-win32', '-mingw32') -config.substitutions.append(('%llc_dwarf', llc_dwarf)) - -# Add site-specific substitutions. -config.substitutions.append(('%gold', config.gold_executable)) -config.substitutions.append(('%go', config.go_executable)) -config.substitutions.append(('%llvmshlibdir', config.llvm_shlib_dir)) -config.substitutions.append(('%shlibext', config.llvm_shlib_ext)) -config.substitutions.append(('%exeext', config.llvm_exe_ext)) -config.substitutions.append(('%python', config.python_executable)) -config.substitutions.append(('%host_cc', config.host_cc)) + llc_args = [' -mtriple=' + + config.target_triple.replace('-win32', '-mingw32')] # Provide the path to asan runtime lib if available. On darwin, this lib needs # to be loaded via DYLD_INSERT_LIBRARIES before libLTO.dylib in case the files @@ -115,36 +115,28 @@ ld64_cmd = config.ld64_executable asan_rtlib = get_asan_rtlib() if asan_rtlib: ld64_cmd = 'DYLD_INSERT_LIBRARIES={} {}'.format(asan_rtlib, ld64_cmd) -config.substitutions.append(('%ld64', ld64_cmd)) -# OCaml substitutions. -# Support tests for both native and bytecode builds. -config.substitutions.append(('%ocamlc', - '%s ocamlc -cclib -L%s %s' % - (config.ocamlfind_executable, config.llvm_lib_dir, config.ocaml_flags))) +ocamlc_command = '%s ocamlc -cclib -L%s %s' % ( + config.ocamlfind_executable, config.llvm_lib_dir, config.ocaml_flags) +ocamlopt_command = 'true' if config.have_ocamlopt: - config.substitutions.append(('%ocamlopt', - '%s ocamlopt -cclib -L%s -cclib -Wl,-rpath,%s %s' % - (config.ocamlfind_executable, config.llvm_lib_dir, config.llvm_lib_dir, config.ocaml_flags))) -else: - config.substitutions.append(('%ocamlopt', 'true')) - -# For each occurrence of an llvm tool name as its own word, replace it -# with the full path to the build directory holding that tool. This -# ensures that we are testing the tools just built and not some random -# tools that might happen to be in the user's PATH. Thus this list -# includes every tool placed in $(LLVM_OBJ_ROOT)/$(BuildMode)/bin -# (llvm_tools_dir in lit parlance). - -# Avoid matching RUN line fragments that are actually part of -# path names or options or whatever. -# The regex is a pre-assertion to avoid matching a preceding -# dot, hyphen, carat, or slash (.foo, -foo, etc.). Some patterns -# also have a post-assertion to not match a trailing hyphen (foo-). -JUNKCHARS = r".-^/<" - -required_tools = [ - 'lli', 'llvm-ar', 'llvm-as', 'llvm-bcanalyzer', 'llvm-config', 'llvm-cov', + ocamlopt_command = '%s ocamlopt -cclib -L%s -cclib -Wl,-rpath,%s %s' % ( + config.ocamlfind_executable, config.llvm_lib_dir, config.llvm_lib_dir, config.ocaml_flags) + + +tools = [ + ToolSubst('%lli', FindTool('lli'), post='.', extra_args=lli_args), + ToolSubst('%llc_dwarf', FindTool('llc'), extra_args=llc_args), + ToolSubst('%go', config.go_executable, unresolved='ignore'), + ToolSubst('%gold', config.gold_executable, unresolved='ignore'), + ToolSubst('%ld64', ld64_cmd, unresolved='ignore'), + ToolSubst('%ocamlc', ocamlc_command, unresolved='ignore'), + ToolSubst('%ocamlopt', ocamlopt_command, unresolved='ignore'), +] + +# FIXME: Why do we have both `lli` and `%lli` that do slightly different things? +tools.extend([ + 'lli', 'lli-child-target', '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', 'llvm-link', 'llvm-lto', 'llvm-lto2', 'llvm-mc', 'llvm-mcmarkup', @@ -152,28 +144,21 @@ required_tools = [ '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 = [ - '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) + 'yaml-bench', 'verify-uselistorder', + 'bugpoint', 'llc', 'llvm-symbolizer', 'opt', 'sancov', 'sanstats']) + +# The following tools are optional +tools.extend([ + ToolSubst('llvm-go', unresolved='ignore'), + ToolSubst('llvm-mt', unresolved='ignore'), + ToolSubst('Kaleidoscope-Ch3', unresolved='ignore'), + ToolSubst('Kaleidoscope-Ch4', unresolved='ignore'), + ToolSubst('Kaleidoscope-Ch5', unresolved='ignore'), + ToolSubst('Kaleidoscope-Ch6', unresolved='ignore'), + ToolSubst('Kaleidoscope-Ch7', unresolved='ignore'), + ToolSubst('Kaleidoscope-Ch8', unresolved='ignore')]) + +llvm_config.add_tool_substitutions(tools, config.llvm_tools_dir) # Targets diff --git a/llvm/utils/lit/lit/llvm/__init__.py b/llvm/utils/lit/lit/llvm/__init__.py index 4a9249978dd..7a46daf2471 100644 --- a/llvm/utils/lit/lit/llvm/__init__.py +++ b/llvm/utils/lit/lit/llvm/__init__.py @@ -1,51 +1,9 @@ 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 llvm_config = config.LLVMConfig(lit_config, test_config) - diff --git a/llvm/utils/lit/lit/llvm/config.py b/llvm/utils/lit/lit/llvm/config.py index 292b77d3da8..87851b3cb1a 100644 --- a/llvm/utils/lit/lit/llvm/config.py +++ b/llvm/utils/lit/lit/llvm/config.py @@ -5,6 +5,8 @@ import subprocess import sys import lit.util +from lit.llvm.subst import FindTool +from lit.llvm.subst import ToolSubst def binary_feature(on, feature, off_prefix): @@ -225,41 +227,47 @@ class LLVMConfig(object): # -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) + match = tool.resolve(self, search_dirs) - tool_match = re.match(r"^(\\)?((\| )?)\W+b([0-9A-Za-z-_\.]+)\\b\W*$", - tool) - if not tool_match: + # Either no match occurred, or there was an unresolved match that + # is ignored. + if not match: continue - tool_pipe = tool_match.group(2) - tool_name = tool_match.group(4) - tool_path = lit.util.which(tool_name, search_dirs) - if not tool_path: - if warn_missing: - # 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 - - if tool_name == 'llc' and os.environ.get('LLVM_ENABLE_MACHINE_VERIFIER') == '1': - tool_path += ' -verify-machineinstrs' - if tool_name == 'llvm-go': - exe = getattr(self.config, 'go_executable', None) - if exe: - tool_path += ' go=' + exe - - self.config.substitutions.append((tool, tool_pipe + tool_path)) + subst_key, tool_pipe, command = match + + # An unresolved match occurred that can't be ignored. Fail without + # adding any of the previously-discovered substitutions. + if not command: + return False + + substitutions.append((subst_key, tool_pipe + command)) + + 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', command=FindTool( + 'count'), verbatim=True, unresolved='fatal'), + ToolSubst(r'\| \bnot\b', command=FindTool('not'), verbatim=True, unresolved='fatal')] + + self.config.substitutions.append(('%python', sys.executable)) + self.add_tool_substitutions( + tool_patterns, [self.config.llvm_tools_dir]) diff --git a/llvm/utils/lit/lit/llvm/subst.py b/llvm/utils/lit/lit/llvm/subst.py new file mode 100644 index 00000000000..e570f4ebf09 --- /dev/null +++ b/llvm/utils/lit/lit/llvm/subst.py @@ -0,0 +1,140 @@ +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): + self.name = name + + def resolve(self, config, dirs): + command = lit.util.which(self.name, dirs) + if not command: + return None + + if self.name == 'llc' and os.environ.get('LLVM_ENABLE_MACHINE_VERIFIER') == '1': + command += ' -verify-machineinstrs' + elif self.name == '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 + tools. + + 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) + if verbatim: + self.regex = key + 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) + + def wordify(word): + match = wordifier.match(word) + introducer = match.group(1) + word = match.group(2) + 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_match.group(2) + tool_name = tool_match.group(4) + + if isinstance(self.command, FindTool): + command_str = self.command.resolve(config, search_dirs) + else: + command_str = str(self.command) + + if command_str: + if self.extra_args: + command_str = ' '.join([command_str] + self.extra_args) + else: + if self.unresolved == 'warn': + # Warn, but still provide a substitution. + config.lit_config.note( + '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 + config.lit_config.fatal( + '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. + pass + elif self.unresolved == 'ignore': + # By returning None, the caller just assumes there was no + # match in the first place. + return None + else: + raise 'Unexpected value for ToolSubst.unresolved' + + return (self.regex, tool_pipe, command_str) diff --git a/llvm/utils/lit/lit/util.py b/llvm/utils/lit/lit/util.py index 18733e15062..5f20262d4c3 100644 --- a/llvm/utils/lit/lit/util.py +++ b/llvm/utils/lit/lit/util.py @@ -195,7 +195,7 @@ def which(command, paths=None): # Check for absolute match first. if os.path.isfile(command): - return command + return os.path.normpath(command) # Would be nice if Python had a lib function for this. if not paths: @@ -213,7 +213,7 @@ def which(command, paths=None): for ext in pathext: p = os.path.join(path, command + ext) if os.path.exists(p) and not os.path.isdir(p): - return p + return os.path.normpath(p) return None |