summaryrefslogtreecommitdiff
path: root/utils/lit/lit/llvm/subst.py
blob: 3c8db1d31ff20347a0c2e05c0978dbc4a72eaf20 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
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):
        # Check for a user explicitely overriding a tool.  This allows:
        #     llvm-lit -D llc="llc -enable-misched -verify-machineinstrs"
        command = config.lit_config.params.get(self.name)
        if command is None:
            # Then check out search paths.
            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)