reference, declarationdefinition
definition → references, declarations, derived classes, virtual overrides
reference to multiple definitions → definitions
unreferenced
    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
  146
  147
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)
        self.was_resolved = False
        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'
        if command_str:
            self.was_resolved = True
        return (self.regex, tool_pipe, command_str)