• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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