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 explicitly 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__( 39 self, 40 key, 41 command=None, 42 pre=r".-^/\<", 43 post="-.", 44 verbatim=False, 45 unresolved="warn", 46 extra_args=None, 47 ): 48 """Construct a ToolSubst. 49 50 key: The text which is to be substituted. 51 52 command: The command to substitute when the key is matched. By default, 53 this will treat `key` as a tool name and search for it. If it is a 54 string, it is interpreted as an exact path. If it is an instance of 55 FindTool, the specified tool name is searched for on disk. 56 57 pre: If specified, the substitution will not find matches where 58 the character immediately preceding the word-boundary that begins 59 `key` is any of the characters in the string `pre`. 60 61 post: If specified, the substitution will not find matches where 62 the character immediately after the word-boundary that ends `key` 63 is any of the characters specified in the string `post`. 64 65 verbatim: If True, `key` is an exact regex that is passed to the 66 underlying substitution 67 68 unresolved: Action to take if the tool substitution cannot be 69 resolved. Valid values: 70 'warn' - log a warning but add the substitution anyway. 71 'fatal' - Exit the test suite and log a fatal error. 72 'break' - Don't add any of the substitutions from the current 73 group, and return a value indicating a failure. 74 'ignore' - Don't add the substitution, and don't log an error 75 76 extra_args: If specified, represents a list of arguments that will be 77 appended to the tool's substitution. 78 79 """ 80 self.unresolved = unresolved 81 self.extra_args = extra_args 82 self.key = key 83 self.command = command if command is not None else FindTool(key) 84 self.was_resolved = False 85 if verbatim: 86 self.regex = key 87 return 88 89 def not_in(chars, where=""): 90 if not chars: 91 return "" 92 pattern_str = "|".join(re.escape(x) for x in chars) 93 return r"(?{}!({}))".format(where, pattern_str) 94 95 def wordify(word): 96 match = wordifier.match(word) 97 introducer = match.group(1) 98 word = match.group(2) 99 return introducer + r"\b" + word + r"\b" 100 101 self.regex = not_in(pre, "<") + wordify(key) + not_in(post) 102 103 def resolve(self, config, search_dirs): 104 # Extract the tool name from the pattern. This relies on the tool name 105 # being surrounded by \b word match operators. If the pattern starts 106 # with "| ", include it in the string to be 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 ) 129 command_str = os.path.join(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 ) 136 elif self.unresolved == "break": 137 # By returning a valid result with an empty command, the 138 # caller treats this as a failure. 139 pass 140 elif self.unresolved == "ignore": 141 # By returning None, the caller just assumes there was no 142 # match in the first place. 143 return None 144 else: 145 raise "Unexpected value for ToolSubst.unresolved" 146 if command_str: 147 self.was_resolved = True 148 return (self.regex, tool_pipe, command_str) 149