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