1import os 2import re 3 4import lit.util 5 6expr = re.compile(r"^(\\)?((\| )?)\W+b(\S+)\\b\W*$") 7wordifier = re.compile(r"(\W*)(\b[^\b]+\b)") 8 9 10class FindTool(object): 11 def __init__(self, name): 12 self.name = name 13 14 def resolve(self, config, dirs): 15 # Check for a user explicitely overriding a tool. This allows: 16 # llvm-lit -D llc="llc -enable-misched -verify-machineinstrs" 17 command = config.lit_config.params.get(self.name) 18 if command is None: 19 # Then check out search paths. 20 command = lit.util.which(self.name, dirs) 21 if not command: 22 return None 23 24 if self.name == 'llc' and os.environ.get('LLVM_ENABLE_MACHINE_VERIFIER') == '1': 25 command += ' -verify-machineinstrs' 26 return command 27 28 29class ToolSubst(object): 30 """String-like class used to build regex substitution patterns for llvm 31 tools. 32 33 Handles things like adding word-boundary patterns, and filtering 34 characters from the beginning an end of a tool name 35 36 """ 37 38 def __init__(self, key, command=None, pre=r'.-^/\<', post='-.', verbatim=False, 39 unresolved='warn', extra_args=None): 40 """Construct a ToolSubst. 41 42 key: The text which is to be substituted. 43 44 command: The command to substitute when the key is matched. By default, 45 this will treat `key` as a tool name and search for it. If it is 46 a string, it is intereprted as an exact path. If it is an instance of 47 FindTool, the specified tool name is searched for on disk. 48 49 pre: If specified, the substitution will not find matches where 50 the character immediately preceding the word-boundary that begins 51 `key` is any of the characters in the string `pre`. 52 53 post: If specified, the substitution will not find matches where 54 the character immediately after the word-boundary that ends `key` 55 is any of the characters specified in the string `post`. 56 57 verbatim: If True, `key` is an exact regex that is passed to the 58 underlying substitution 59 60 unresolved: Action to take if the tool substitution cannot be 61 resolved. Valid values: 62 'warn' - log a warning but add the substitution anyway. 63 'fatal' - Exit the test suite and log a fatal error. 64 'break' - Don't add any of the substitutions from the current 65 group, and return a value indicating a failure. 66 'ignore' - Don't add the substitution, and don't log an error 67 68 extra_args: If specified, represents a list of arguments that will be 69 appended to the tool's substitution. 70 71 explicit_path: If specified, the exact path will be used as a substitution. 72 Otherwise, the tool will be searched for as if by calling which(tool) 73 74 """ 75 self.unresolved = unresolved 76 self.extra_args = extra_args 77 self.key = key 78 self.command = command if command is not None else FindTool(key) 79 self.was_resolved = False 80 if verbatim: 81 self.regex = key 82 return 83 84 def not_in(chars, where=''): 85 if not chars: 86 return '' 87 pattern_str = '|'.join(re.escape(x) for x in chars) 88 return r'(?{}!({}))'.format(where, pattern_str) 89 90 def wordify(word): 91 match = wordifier.match(word) 92 introducer = match.group(1) 93 word = match.group(2) 94 return introducer + r'\b' + word + r'\b' 95 96 self.regex = not_in(pre, '<') + wordify(key) + not_in(post) 97 98 def resolve(self, config, search_dirs): 99 # Extract the tool name from the pattern. This relies on the tool 100 # name being surrounded by \b word match operators. If the 101 # pattern starts with "| ", include it in the string to be 102 # substituted. 103 104 tool_match = expr.match(self.regex) 105 if not tool_match: 106 return None 107 108 tool_pipe = tool_match.group(2) 109 tool_name = tool_match.group(4) 110 111 if isinstance(self.command, FindTool): 112 command_str = self.command.resolve(config, search_dirs) 113 else: 114 command_str = str(self.command) 115 116 if command_str: 117 if self.extra_args: 118 command_str = ' '.join([command_str] + self.extra_args) 119 else: 120 if self.unresolved == 'warn': 121 # Warn, but still provide a substitution. 122 config.lit_config.note( 123 'Did not find ' + tool_name + ' in %s' % search_dirs) 124 command_str = os.path.join( 125 config.config.llvm_tools_dir, tool_name) 126 elif self.unresolved == 'fatal': 127 # The function won't even return in this case, this leads to 128 # sys.exit 129 config.lit_config.fatal( 130 'Did not find ' + tool_name + ' in %s' % search_dirs) 131 elif self.unresolved == 'break': 132 # By returning a valid result with an empty command, the 133 # caller treats this as a failure. 134 pass 135 elif self.unresolved == 'ignore': 136 # By returning None, the caller just assumes there was no 137 # match in the first place. 138 return None 139 else: 140 raise 'Unexpected value for ToolSubst.unresolved' 141 if command_str: 142 self.was_resolved = True 143 return (self.regex, tool_pipe, command_str) 144