You've already forked linux-packaging-mono
Imported Upstream version 5.18.0.167
Former-commit-id: 289509151e0fee68a1b591a20c9f109c3c789d3a
This commit is contained in:
parent
e19d552987
commit
b084638f15
251
external/llvm/utils/lit/lit/BooleanExpression.py
vendored
251
external/llvm/utils/lit/lit/BooleanExpression.py
vendored
@ -1,251 +0,0 @@
|
||||
import re
|
||||
|
||||
class BooleanExpression:
|
||||
# A simple evaluator of boolean expressions.
|
||||
#
|
||||
# Grammar:
|
||||
# expr :: or_expr
|
||||
# or_expr :: and_expr ('||' and_expr)*
|
||||
# and_expr :: not_expr ('&&' not_expr)*
|
||||
# not_expr :: '!' not_expr
|
||||
# '(' or_expr ')'
|
||||
# identifier
|
||||
# identifier :: [-+=._a-zA-Z0-9]+
|
||||
|
||||
# Evaluates `string` as a boolean expression.
|
||||
# Returns True or False. Throws a ValueError on syntax error.
|
||||
#
|
||||
# Variables in `variables` are true.
|
||||
# Substrings of `triple` are true.
|
||||
# 'true' is true.
|
||||
# All other identifiers are false.
|
||||
@staticmethod
|
||||
def evaluate(string, variables, triple=""):
|
||||
try:
|
||||
parser = BooleanExpression(string, set(variables), triple)
|
||||
return parser.parseAll()
|
||||
except ValueError as e:
|
||||
raise ValueError(str(e) + ('\nin expression: %r' % string))
|
||||
|
||||
#####
|
||||
|
||||
def __init__(self, string, variables, triple=""):
|
||||
self.tokens = BooleanExpression.tokenize(string)
|
||||
self.variables = variables
|
||||
self.variables.add('true')
|
||||
self.triple = triple
|
||||
self.value = None
|
||||
self.token = None
|
||||
|
||||
# Singleton end-of-expression marker.
|
||||
END = object()
|
||||
|
||||
# Tokenization pattern.
|
||||
Pattern = re.compile(r'\A\s*([()]|[-+=._a-zA-Z0-9]+|&&|\|\||!)\s*(.*)\Z')
|
||||
|
||||
@staticmethod
|
||||
def tokenize(string):
|
||||
while True:
|
||||
m = re.match(BooleanExpression.Pattern, string)
|
||||
if m is None:
|
||||
if string == "":
|
||||
yield BooleanExpression.END;
|
||||
return
|
||||
else:
|
||||
raise ValueError("couldn't parse text: %r" % string)
|
||||
|
||||
token = m.group(1)
|
||||
string = m.group(2)
|
||||
yield token
|
||||
|
||||
def quote(self, token):
|
||||
if token is BooleanExpression.END:
|
||||
return '<end of expression>'
|
||||
else:
|
||||
return repr(token)
|
||||
|
||||
def accept(self, t):
|
||||
if self.token == t:
|
||||
self.token = next(self.tokens)
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def expect(self, t):
|
||||
if self.token == t:
|
||||
if self.token != BooleanExpression.END:
|
||||
self.token = next(self.tokens)
|
||||
else:
|
||||
raise ValueError("expected: %s\nhave: %s" %
|
||||
(self.quote(t), self.quote(self.token)))
|
||||
|
||||
def isIdentifier(self, t):
|
||||
if (t is BooleanExpression.END or t == '&&' or t == '||' or
|
||||
t == '!' or t == '(' or t == ')'):
|
||||
return False
|
||||
return True
|
||||
|
||||
def parseNOT(self):
|
||||
if self.accept('!'):
|
||||
self.parseNOT()
|
||||
self.value = not self.value
|
||||
elif self.accept('('):
|
||||
self.parseOR()
|
||||
self.expect(')')
|
||||
elif not self.isIdentifier(self.token):
|
||||
raise ValueError("expected: '!' or '(' or identifier\nhave: %s" %
|
||||
self.quote(self.token))
|
||||
else:
|
||||
self.value = (self.token in self.variables or
|
||||
self.token in self.triple)
|
||||
self.token = next(self.tokens)
|
||||
|
||||
def parseAND(self):
|
||||
self.parseNOT()
|
||||
while self.accept('&&'):
|
||||
left = self.value
|
||||
self.parseNOT()
|
||||
right = self.value
|
||||
# this is technically the wrong associativity, but it
|
||||
# doesn't matter for this limited expression grammar
|
||||
self.value = left and right
|
||||
|
||||
def parseOR(self):
|
||||
self.parseAND()
|
||||
while self.accept('||'):
|
||||
left = self.value
|
||||
self.parseAND()
|
||||
right = self.value
|
||||
# this is technically the wrong associativity, but it
|
||||
# doesn't matter for this limited expression grammar
|
||||
self.value = left or right
|
||||
|
||||
def parseAll(self):
|
||||
self.token = next(self.tokens)
|
||||
self.parseOR()
|
||||
self.expect(BooleanExpression.END)
|
||||
return self.value
|
||||
|
||||
|
||||
#######
|
||||
# Tests
|
||||
|
||||
import unittest
|
||||
|
||||
class TestBooleanExpression(unittest.TestCase):
|
||||
def test_variables(self):
|
||||
variables = {'its-true', 'false-lol-true', 'under_score',
|
||||
'e=quals', 'd1g1ts'}
|
||||
self.assertTrue(BooleanExpression.evaluate('true', variables))
|
||||
self.assertTrue(BooleanExpression.evaluate('its-true', variables))
|
||||
self.assertTrue(BooleanExpression.evaluate('false-lol-true', variables))
|
||||
self.assertTrue(BooleanExpression.evaluate('under_score', variables))
|
||||
self.assertTrue(BooleanExpression.evaluate('e=quals', variables))
|
||||
self.assertTrue(BooleanExpression.evaluate('d1g1ts', variables))
|
||||
|
||||
self.assertFalse(BooleanExpression.evaluate('false', variables))
|
||||
self.assertFalse(BooleanExpression.evaluate('True', variables))
|
||||
self.assertFalse(BooleanExpression.evaluate('true-ish', variables))
|
||||
self.assertFalse(BooleanExpression.evaluate('not_true', variables))
|
||||
self.assertFalse(BooleanExpression.evaluate('tru', variables))
|
||||
|
||||
def test_triple(self):
|
||||
triple = 'arch-vendor-os'
|
||||
self.assertTrue(BooleanExpression.evaluate('arch-', {}, triple))
|
||||
self.assertTrue(BooleanExpression.evaluate('ar', {}, triple))
|
||||
self.assertTrue(BooleanExpression.evaluate('ch-vend', {}, triple))
|
||||
self.assertTrue(BooleanExpression.evaluate('-vendor-', {}, triple))
|
||||
self.assertTrue(BooleanExpression.evaluate('-os', {}, triple))
|
||||
self.assertFalse(BooleanExpression.evaluate('arch-os', {}, triple))
|
||||
|
||||
def test_operators(self):
|
||||
self.assertTrue(BooleanExpression.evaluate('true || true', {}))
|
||||
self.assertTrue(BooleanExpression.evaluate('true || false', {}))
|
||||
self.assertTrue(BooleanExpression.evaluate('false || true', {}))
|
||||
self.assertFalse(BooleanExpression.evaluate('false || false', {}))
|
||||
|
||||
self.assertTrue(BooleanExpression.evaluate('true && true', {}))
|
||||
self.assertFalse(BooleanExpression.evaluate('true && false', {}))
|
||||
self.assertFalse(BooleanExpression.evaluate('false && true', {}))
|
||||
self.assertFalse(BooleanExpression.evaluate('false && false', {}))
|
||||
|
||||
self.assertFalse(BooleanExpression.evaluate('!true', {}))
|
||||
self.assertTrue(BooleanExpression.evaluate('!false', {}))
|
||||
|
||||
self.assertTrue(BooleanExpression.evaluate(' ((!((false) )) ) ', {}))
|
||||
self.assertTrue(BooleanExpression.evaluate('true && (true && (true))', {}))
|
||||
self.assertTrue(BooleanExpression.evaluate('!false && !false && !! !false', {}))
|
||||
self.assertTrue(BooleanExpression.evaluate('false && false || true', {}))
|
||||
self.assertTrue(BooleanExpression.evaluate('(false && false) || true', {}))
|
||||
self.assertFalse(BooleanExpression.evaluate('false && (false || true)', {}))
|
||||
|
||||
# Evaluate boolean expression `expr`.
|
||||
# Fail if it does not throw a ValueError containing the text `error`.
|
||||
def checkException(self, expr, error):
|
||||
try:
|
||||
BooleanExpression.evaluate(expr, {})
|
||||
self.fail("expression %r didn't cause an exception" % expr)
|
||||
except ValueError as e:
|
||||
if -1 == str(e).find(error):
|
||||
self.fail(("expression %r caused the wrong ValueError\n" +
|
||||
"actual error was:\n%s\n" +
|
||||
"expected error was:\n%s\n") % (expr, e, error))
|
||||
except BaseException as e:
|
||||
self.fail(("expression %r caused the wrong exception; actual " +
|
||||
"exception was: \n%r") % (expr, e))
|
||||
|
||||
def test_errors(self):
|
||||
self.checkException("ba#d",
|
||||
"couldn't parse text: '#d'\n" +
|
||||
"in expression: 'ba#d'")
|
||||
|
||||
self.checkException("true and true",
|
||||
"expected: <end of expression>\n" +
|
||||
"have: 'and'\n" +
|
||||
"in expression: 'true and true'")
|
||||
|
||||
self.checkException("|| true",
|
||||
"expected: '!' or '(' or identifier\n" +
|
||||
"have: '||'\n" +
|
||||
"in expression: '|| true'")
|
||||
|
||||
self.checkException("true &&",
|
||||
"expected: '!' or '(' or identifier\n" +
|
||||
"have: <end of expression>\n" +
|
||||
"in expression: 'true &&'")
|
||||
|
||||
self.checkException("",
|
||||
"expected: '!' or '(' or identifier\n" +
|
||||
"have: <end of expression>\n" +
|
||||
"in expression: ''")
|
||||
|
||||
self.checkException("*",
|
||||
"couldn't parse text: '*'\n" +
|
||||
"in expression: '*'")
|
||||
|
||||
self.checkException("no wait stop",
|
||||
"expected: <end of expression>\n" +
|
||||
"have: 'wait'\n" +
|
||||
"in expression: 'no wait stop'")
|
||||
|
||||
self.checkException("no-$-please",
|
||||
"couldn't parse text: '$-please'\n" +
|
||||
"in expression: 'no-$-please'")
|
||||
|
||||
self.checkException("(((true && true) || true)",
|
||||
"expected: ')'\n" +
|
||||
"have: <end of expression>\n" +
|
||||
"in expression: '(((true && true) || true)'")
|
||||
|
||||
self.checkException("true (true)",
|
||||
"expected: <end of expression>\n" +
|
||||
"have: '('\n" +
|
||||
"in expression: 'true (true)'")
|
||||
|
||||
self.checkException("( )",
|
||||
"expected: '!' or '(' or identifier\n" +
|
||||
"have: ')'\n" +
|
||||
"in expression: '( )'")
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
@ -1,15 +0,0 @@
|
||||
# -*- Python -*-
|
||||
|
||||
# Site specific configuration file.
|
||||
#
|
||||
# Typically this will be generated by the build system to automatically set
|
||||
# certain configuration variables which cannot be autodetected, so that 'lit'
|
||||
# can easily be used on the command line.
|
||||
|
||||
import os
|
||||
|
||||
# Preserve the obj_root, for use by the main lit.cfg.
|
||||
config.example_obj_root = os.path.dirname(__file__)
|
||||
|
||||
lit.load_config(config, os.path.join(config.test_source_root,
|
||||
'lit.cfg'))
|
161
external/llvm/utils/lit/lit/LitConfig.py
vendored
161
external/llvm/utils/lit/lit/LitConfig.py
vendored
@ -1,161 +0,0 @@
|
||||
from __future__ import absolute_import
|
||||
import inspect
|
||||
import os
|
||||
import sys
|
||||
|
||||
import lit.Test
|
||||
import lit.formats
|
||||
import lit.TestingConfig
|
||||
import lit.util
|
||||
|
||||
# LitConfig must be a new style class for properties to work
|
||||
class LitConfig(object):
|
||||
"""LitConfig - Configuration data for a 'lit' test runner instance, shared
|
||||
across all tests.
|
||||
|
||||
The LitConfig object is also used to communicate with client configuration
|
||||
files, it is always passed in as the global variable 'lit' so that
|
||||
configuration files can access common functionality and internal components
|
||||
easily.
|
||||
"""
|
||||
|
||||
def __init__(self, progname, path, quiet,
|
||||
useValgrind, valgrindLeakCheck, valgrindArgs,
|
||||
noExecute, debug, isWindows, singleProcess,
|
||||
params, config_prefix = None,
|
||||
maxIndividualTestTime = 0,
|
||||
maxFailures = None,
|
||||
parallelism_groups = {},
|
||||
echo_all_commands = False):
|
||||
# The name of the test runner.
|
||||
self.progname = progname
|
||||
# The items to add to the PATH environment variable.
|
||||
self.path = [str(p) for p in path]
|
||||
self.quiet = bool(quiet)
|
||||
self.useValgrind = bool(useValgrind)
|
||||
self.valgrindLeakCheck = bool(valgrindLeakCheck)
|
||||
self.valgrindUserArgs = list(valgrindArgs)
|
||||
self.noExecute = noExecute
|
||||
self.debug = debug
|
||||
self.singleProcess = singleProcess
|
||||
self.isWindows = bool(isWindows)
|
||||
self.params = dict(params)
|
||||
self.bashPath = None
|
||||
|
||||
# Configuration files to look for when discovering test suites.
|
||||
self.config_prefix = config_prefix or 'lit'
|
||||
self.suffixes = ['cfg.py', 'cfg']
|
||||
self.config_names = ['%s.%s' % (self.config_prefix,x) for x in self.suffixes]
|
||||
self.site_config_names = ['%s.site.%s' % (self.config_prefix,x) for x in self.suffixes]
|
||||
self.local_config_names = ['%s.local.%s' % (self.config_prefix,x) for x in self.suffixes]
|
||||
|
||||
self.numErrors = 0
|
||||
self.numWarnings = 0
|
||||
|
||||
self.valgrindArgs = []
|
||||
if self.useValgrind:
|
||||
self.valgrindArgs = ['valgrind', '-q', '--run-libc-freeres=no',
|
||||
'--tool=memcheck', '--trace-children=yes',
|
||||
'--error-exitcode=123']
|
||||
if self.valgrindLeakCheck:
|
||||
self.valgrindArgs.append('--leak-check=full')
|
||||
else:
|
||||
# The default is 'summary'.
|
||||
self.valgrindArgs.append('--leak-check=no')
|
||||
self.valgrindArgs.extend(self.valgrindUserArgs)
|
||||
|
||||
self.maxIndividualTestTime = maxIndividualTestTime
|
||||
self.maxFailures = maxFailures
|
||||
self.parallelism_groups = parallelism_groups
|
||||
self.echo_all_commands = echo_all_commands
|
||||
|
||||
@property
|
||||
def maxIndividualTestTime(self):
|
||||
"""
|
||||
Interface for getting maximum time to spend executing
|
||||
a single test
|
||||
"""
|
||||
return self._maxIndividualTestTime
|
||||
|
||||
@maxIndividualTestTime.setter
|
||||
def maxIndividualTestTime(self, value):
|
||||
"""
|
||||
Interface for setting maximum time to spend executing
|
||||
a single test
|
||||
"""
|
||||
self._maxIndividualTestTime = value
|
||||
if self.maxIndividualTestTime > 0:
|
||||
# The current implementation needs psutil to set
|
||||
# a timeout per test. Check it's available.
|
||||
# See lit.util.killProcessAndChildren()
|
||||
try:
|
||||
import psutil # noqa: F401
|
||||
except ImportError:
|
||||
self.fatal("Setting a timeout per test requires the"
|
||||
" Python psutil module but it could not be"
|
||||
" found. Try installing it via pip or via"
|
||||
" your operating system's package manager.")
|
||||
elif self.maxIndividualTestTime < 0:
|
||||
self.fatal('The timeout per test must be >= 0 seconds')
|
||||
|
||||
def load_config(self, config, path):
|
||||
"""load_config(config, path) - Load a config object from an alternate
|
||||
path."""
|
||||
if self.debug:
|
||||
self.note('load_config from %r' % path)
|
||||
config.load_from_path(path, self)
|
||||
return config
|
||||
|
||||
def getBashPath(self):
|
||||
"""getBashPath - Get the path to 'bash'"""
|
||||
if self.bashPath is not None:
|
||||
return self.bashPath
|
||||
|
||||
self.bashPath = lit.util.which('bash', os.pathsep.join(self.path))
|
||||
if self.bashPath is None:
|
||||
self.bashPath = lit.util.which('bash')
|
||||
|
||||
if self.bashPath is None:
|
||||
self.bashPath = ''
|
||||
|
||||
return self.bashPath
|
||||
|
||||
def getToolsPath(self, dir, paths, tools):
|
||||
if dir is not None and os.path.isabs(dir) and os.path.isdir(dir):
|
||||
if not lit.util.checkToolsPath(dir, tools):
|
||||
return None
|
||||
else:
|
||||
dir = lit.util.whichTools(tools, paths)
|
||||
|
||||
# bash
|
||||
self.bashPath = lit.util.which('bash', dir)
|
||||
if self.bashPath is None:
|
||||
self.bashPath = ''
|
||||
|
||||
return dir
|
||||
|
||||
def _write_message(self, kind, message):
|
||||
# Get the file/line where this message was generated.
|
||||
f = inspect.currentframe()
|
||||
# Step out of _write_message, and then out of wrapper.
|
||||
f = f.f_back.f_back
|
||||
file,line,_,_,_ = inspect.getframeinfo(f)
|
||||
location = '%s:%d' % (file, line)
|
||||
|
||||
sys.stderr.write('%s: %s: %s: %s\n' % (self.progname, location,
|
||||
kind, message))
|
||||
|
||||
def note(self, message):
|
||||
self._write_message('note', message)
|
||||
|
||||
def warning(self, message):
|
||||
self._write_message('warning', message)
|
||||
self.numWarnings += 1
|
||||
|
||||
def error(self, message):
|
||||
self._write_message('error', message)
|
||||
self.numErrors += 1
|
||||
|
||||
def fatal(self, message):
|
||||
self._write_message('fatal', message)
|
||||
sys.exit(2)
|
34
external/llvm/utils/lit/lit/LitTestCase.py
vendored
34
external/llvm/utils/lit/lit/LitTestCase.py
vendored
@ -1,34 +0,0 @@
|
||||
from __future__ import absolute_import
|
||||
import unittest
|
||||
|
||||
import lit.Test
|
||||
|
||||
"""
|
||||
TestCase adaptor for providing a 'unittest' compatible interface to 'lit' tests.
|
||||
"""
|
||||
|
||||
class UnresolvedError(RuntimeError):
|
||||
pass
|
||||
|
||||
class LitTestCase(unittest.TestCase):
|
||||
def __init__(self, test, run):
|
||||
unittest.TestCase.__init__(self)
|
||||
self._test = test
|
||||
self._run = run
|
||||
|
||||
def id(self):
|
||||
return self._test.getFullName()
|
||||
|
||||
def shortDescription(self):
|
||||
return self._test.getFullName()
|
||||
|
||||
def runTest(self):
|
||||
# Run the test.
|
||||
self._run.execute_test(self._test)
|
||||
|
||||
# Adapt the result to unittest.
|
||||
result = self._test.result
|
||||
if result.code is lit.Test.UNRESOLVED:
|
||||
raise UnresolvedError(result.output)
|
||||
elif result.code.isFailure:
|
||||
self.fail(result.output)
|
291
external/llvm/utils/lit/lit/ProgressBar.py
vendored
291
external/llvm/utils/lit/lit/ProgressBar.py
vendored
@ -1,291 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# Source: http://code.activestate.com/recipes/475116/, with
|
||||
# modifications by Daniel Dunbar.
|
||||
|
||||
import sys, re, time
|
||||
|
||||
def to_bytes(str):
|
||||
# Encode to UTF-8 to get binary data.
|
||||
return str.encode('utf-8')
|
||||
|
||||
class TerminalController:
|
||||
"""
|
||||
A class that can be used to portably generate formatted output to
|
||||
a terminal.
|
||||
|
||||
`TerminalController` defines a set of instance variables whose
|
||||
values are initialized to the control sequence necessary to
|
||||
perform a given action. These can be simply included in normal
|
||||
output to the terminal:
|
||||
|
||||
>>> term = TerminalController()
|
||||
>>> print('This is '+term.GREEN+'green'+term.NORMAL)
|
||||
|
||||
Alternatively, the `render()` method can used, which replaces
|
||||
'${action}' with the string required to perform 'action':
|
||||
|
||||
>>> term = TerminalController()
|
||||
>>> print(term.render('This is ${GREEN}green${NORMAL}'))
|
||||
|
||||
If the terminal doesn't support a given action, then the value of
|
||||
the corresponding instance variable will be set to ''. As a
|
||||
result, the above code will still work on terminals that do not
|
||||
support color, except that their output will not be colored.
|
||||
Also, this means that you can test whether the terminal supports a
|
||||
given action by simply testing the truth value of the
|
||||
corresponding instance variable:
|
||||
|
||||
>>> term = TerminalController()
|
||||
>>> if term.CLEAR_SCREEN:
|
||||
... print('This terminal supports clearning the screen.')
|
||||
|
||||
Finally, if the width and height of the terminal are known, then
|
||||
they will be stored in the `COLS` and `LINES` attributes.
|
||||
"""
|
||||
# Cursor movement:
|
||||
BOL = '' #: Move the cursor to the beginning of the line
|
||||
UP = '' #: Move the cursor up one line
|
||||
DOWN = '' #: Move the cursor down one line
|
||||
LEFT = '' #: Move the cursor left one char
|
||||
RIGHT = '' #: Move the cursor right one char
|
||||
|
||||
# Deletion:
|
||||
CLEAR_SCREEN = '' #: Clear the screen and move to home position
|
||||
CLEAR_EOL = '' #: Clear to the end of the line.
|
||||
CLEAR_BOL = '' #: Clear to the beginning of the line.
|
||||
CLEAR_EOS = '' #: Clear to the end of the screen
|
||||
|
||||
# Output modes:
|
||||
BOLD = '' #: Turn on bold mode
|
||||
BLINK = '' #: Turn on blink mode
|
||||
DIM = '' #: Turn on half-bright mode
|
||||
REVERSE = '' #: Turn on reverse-video mode
|
||||
NORMAL = '' #: Turn off all modes
|
||||
|
||||
# Cursor display:
|
||||
HIDE_CURSOR = '' #: Make the cursor invisible
|
||||
SHOW_CURSOR = '' #: Make the cursor visible
|
||||
|
||||
# Terminal size:
|
||||
COLS = None #: Width of the terminal (None for unknown)
|
||||
LINES = None #: Height of the terminal (None for unknown)
|
||||
|
||||
# Foreground colors:
|
||||
BLACK = BLUE = GREEN = CYAN = RED = MAGENTA = YELLOW = WHITE = ''
|
||||
|
||||
# Background colors:
|
||||
BG_BLACK = BG_BLUE = BG_GREEN = BG_CYAN = ''
|
||||
BG_RED = BG_MAGENTA = BG_YELLOW = BG_WHITE = ''
|
||||
|
||||
_STRING_CAPABILITIES = """
|
||||
BOL=cr UP=cuu1 DOWN=cud1 LEFT=cub1 RIGHT=cuf1
|
||||
CLEAR_SCREEN=clear CLEAR_EOL=el CLEAR_BOL=el1 CLEAR_EOS=ed BOLD=bold
|
||||
BLINK=blink DIM=dim REVERSE=rev UNDERLINE=smul NORMAL=sgr0
|
||||
HIDE_CURSOR=cinvis SHOW_CURSOR=cnorm""".split()
|
||||
_COLORS = """BLACK BLUE GREEN CYAN RED MAGENTA YELLOW WHITE""".split()
|
||||
_ANSICOLORS = "BLACK RED GREEN YELLOW BLUE MAGENTA CYAN WHITE".split()
|
||||
|
||||
def __init__(self, term_stream=sys.stdout):
|
||||
"""
|
||||
Create a `TerminalController` and initialize its attributes
|
||||
with appropriate values for the current terminal.
|
||||
`term_stream` is the stream that will be used for terminal
|
||||
output; if this stream is not a tty, then the terminal is
|
||||
assumed to be a dumb terminal (i.e., have no capabilities).
|
||||
"""
|
||||
# Curses isn't available on all platforms
|
||||
try: import curses
|
||||
except: return
|
||||
|
||||
# If the stream isn't a tty, then assume it has no capabilities.
|
||||
if not term_stream.isatty(): return
|
||||
|
||||
# Check the terminal type. If we fail, then assume that the
|
||||
# terminal has no capabilities.
|
||||
try: curses.setupterm()
|
||||
except: return
|
||||
|
||||
# Look up numeric capabilities.
|
||||
self.COLS = curses.tigetnum('cols')
|
||||
self.LINES = curses.tigetnum('lines')
|
||||
self.XN = curses.tigetflag('xenl')
|
||||
|
||||
# Look up string capabilities.
|
||||
for capability in self._STRING_CAPABILITIES:
|
||||
(attrib, cap_name) = capability.split('=')
|
||||
setattr(self, attrib, self._tigetstr(cap_name) or '')
|
||||
|
||||
# Colors
|
||||
set_fg = self._tigetstr('setf')
|
||||
if set_fg:
|
||||
for i,color in zip(range(len(self._COLORS)), self._COLORS):
|
||||
setattr(self, color, self._tparm(set_fg, i))
|
||||
set_fg_ansi = self._tigetstr('setaf')
|
||||
if set_fg_ansi:
|
||||
for i,color in zip(range(len(self._ANSICOLORS)), self._ANSICOLORS):
|
||||
setattr(self, color, self._tparm(set_fg_ansi, i))
|
||||
set_bg = self._tigetstr('setb')
|
||||
if set_bg:
|
||||
for i,color in zip(range(len(self._COLORS)), self._COLORS):
|
||||
setattr(self, 'BG_'+color, self._tparm(set_bg, i))
|
||||
set_bg_ansi = self._tigetstr('setab')
|
||||
if set_bg_ansi:
|
||||
for i,color in zip(range(len(self._ANSICOLORS)), self._ANSICOLORS):
|
||||
setattr(self, 'BG_'+color, self._tparm(set_bg_ansi, i))
|
||||
|
||||
def _tparm(self, arg, index):
|
||||
import curses
|
||||
return curses.tparm(to_bytes(arg), index).decode('utf-8') or ''
|
||||
|
||||
def _tigetstr(self, cap_name):
|
||||
# String capabilities can include "delays" of the form "$<2>".
|
||||
# For any modern terminal, we should be able to just ignore
|
||||
# these, so strip them out.
|
||||
import curses
|
||||
cap = curses.tigetstr(cap_name)
|
||||
if cap is None:
|
||||
cap = ''
|
||||
else:
|
||||
cap = cap.decode('utf-8')
|
||||
return re.sub(r'\$<\d+>[/*]?', '', cap)
|
||||
|
||||
def render(self, template):
|
||||
"""
|
||||
Replace each $-substitutions in the given template string with
|
||||
the corresponding terminal control string (if it's defined) or
|
||||
'' (if it's not).
|
||||
"""
|
||||
return re.sub(r'\$\$|\${\w+}', self._render_sub, template)
|
||||
|
||||
def _render_sub(self, match):
|
||||
s = match.group()
|
||||
if s == '$$': return s
|
||||
else: return getattr(self, s[2:-1])
|
||||
|
||||
#######################################################################
|
||||
# Example use case: progress bar
|
||||
#######################################################################
|
||||
|
||||
class SimpleProgressBar:
|
||||
"""
|
||||
A simple progress bar which doesn't need any terminal support.
|
||||
|
||||
This prints out a progress bar like:
|
||||
'Header: 0 .. 10.. 20.. ...'
|
||||
"""
|
||||
|
||||
def __init__(self, header):
|
||||
self.header = header
|
||||
self.atIndex = None
|
||||
|
||||
def update(self, percent, message):
|
||||
if self.atIndex is None:
|
||||
sys.stdout.write(self.header)
|
||||
self.atIndex = 0
|
||||
|
||||
next = int(percent*50)
|
||||
if next == self.atIndex:
|
||||
return
|
||||
|
||||
for i in range(self.atIndex, next):
|
||||
idx = i % 5
|
||||
if idx == 0:
|
||||
sys.stdout.write('%-2d' % (i*2))
|
||||
elif idx == 1:
|
||||
pass # Skip second char
|
||||
elif idx < 4:
|
||||
sys.stdout.write('.')
|
||||
else:
|
||||
sys.stdout.write(' ')
|
||||
sys.stdout.flush()
|
||||
self.atIndex = next
|
||||
|
||||
def clear(self):
|
||||
if self.atIndex is not None:
|
||||
sys.stdout.write('\n')
|
||||
sys.stdout.flush()
|
||||
self.atIndex = None
|
||||
|
||||
class ProgressBar:
|
||||
"""
|
||||
A 3-line progress bar, which looks like::
|
||||
|
||||
Header
|
||||
20% [===========----------------------------------]
|
||||
progress message
|
||||
|
||||
The progress bar is colored, if the terminal supports color
|
||||
output; and adjusts to the width of the terminal.
|
||||
"""
|
||||
BAR = '%s${GREEN}[${BOLD}%s%s${NORMAL}${GREEN}]${NORMAL}%s'
|
||||
HEADER = '${BOLD}${CYAN}%s${NORMAL}\n\n'
|
||||
|
||||
def __init__(self, term, header, useETA=True):
|
||||
self.term = term
|
||||
if not (self.term.CLEAR_EOL and self.term.UP and self.term.BOL):
|
||||
raise ValueError("Terminal isn't capable enough -- you "
|
||||
"should use a simpler progress dispaly.")
|
||||
self.BOL = self.term.BOL # BoL from col#79
|
||||
self.XNL = "\n" # Newline from col#79
|
||||
if self.term.COLS:
|
||||
self.width = self.term.COLS
|
||||
if not self.term.XN:
|
||||
self.BOL = self.term.UP + self.term.BOL
|
||||
self.XNL = "" # Cursor must be fed to the next line
|
||||
else:
|
||||
self.width = 75
|
||||
self.bar = term.render(self.BAR)
|
||||
self.header = self.term.render(self.HEADER % header.center(self.width))
|
||||
self.cleared = 1 #: true if we haven't drawn the bar yet.
|
||||
self.useETA = useETA
|
||||
if self.useETA:
|
||||
self.startTime = time.time()
|
||||
self.update(0, '')
|
||||
|
||||
def update(self, percent, message):
|
||||
if self.cleared:
|
||||
sys.stdout.write(self.header)
|
||||
self.cleared = 0
|
||||
prefix = '%3d%% ' % (percent*100,)
|
||||
suffix = ''
|
||||
if self.useETA:
|
||||
elapsed = time.time() - self.startTime
|
||||
if percent > .0001 and elapsed > 1:
|
||||
total = elapsed / percent
|
||||
eta = int(total - elapsed)
|
||||
h = eta//3600.
|
||||
m = (eta//60) % 60
|
||||
s = eta % 60
|
||||
suffix = ' ETA: %02d:%02d:%02d'%(h,m,s)
|
||||
barWidth = self.width - len(prefix) - len(suffix) - 2
|
||||
n = int(barWidth*percent)
|
||||
if len(message) < self.width:
|
||||
message = message + ' '*(self.width - len(message))
|
||||
else:
|
||||
message = '... ' + message[-(self.width-4):]
|
||||
sys.stdout.write(
|
||||
self.BOL + self.term.UP + self.term.CLEAR_EOL +
|
||||
(self.bar % (prefix, '='*n, '-'*(barWidth-n), suffix)) +
|
||||
self.XNL +
|
||||
self.term.CLEAR_EOL + message)
|
||||
if not self.term.XN:
|
||||
sys.stdout.flush()
|
||||
|
||||
def clear(self):
|
||||
if not self.cleared:
|
||||
sys.stdout.write(self.BOL + self.term.CLEAR_EOL +
|
||||
self.term.UP + self.term.CLEAR_EOL +
|
||||
self.term.UP + self.term.CLEAR_EOL)
|
||||
sys.stdout.flush()
|
||||
self.cleared = 1
|
||||
|
||||
def test():
|
||||
tc = TerminalController()
|
||||
p = ProgressBar(tc, 'Tests')
|
||||
for i in range(101):
|
||||
p.update(i/100., str(i))
|
||||
time.sleep(.3)
|
||||
|
||||
if __name__=='__main__':
|
||||
test()
|
108
external/llvm/utils/lit/lit/ShCommands.py
vendored
108
external/llvm/utils/lit/lit/ShCommands.py
vendored
@ -1,108 +0,0 @@
|
||||
class Command:
|
||||
def __init__(self, args, redirects):
|
||||
self.args = list(args)
|
||||
self.redirects = list(redirects)
|
||||
|
||||
def __repr__(self):
|
||||
return 'Command(%r, %r)' % (self.args, self.redirects)
|
||||
|
||||
def __eq__(self, other):
|
||||
if not isinstance(other, Command):
|
||||
return False
|
||||
|
||||
return ((self.args, self.redirects) ==
|
||||
(other.args, other.redirects))
|
||||
|
||||
def toShell(self, file):
|
||||
for arg in self.args:
|
||||
if "'" not in arg:
|
||||
quoted = "'%s'" % arg
|
||||
elif '"' not in arg and '$' not in arg:
|
||||
quoted = '"%s"' % arg
|
||||
else:
|
||||
raise NotImplementedError('Unable to quote %r' % arg)
|
||||
file.write(quoted)
|
||||
|
||||
# For debugging / validation.
|
||||
import ShUtil
|
||||
dequoted = list(ShUtil.ShLexer(quoted).lex())
|
||||
if dequoted != [arg]:
|
||||
raise NotImplementedError('Unable to quote %r' % arg)
|
||||
|
||||
for r in self.redirects:
|
||||
if len(r[0]) == 1:
|
||||
file.write("%s '%s'" % (r[0][0], r[1]))
|
||||
else:
|
||||
file.write("%s%s '%s'" % (r[0][1], r[0][0], r[1]))
|
||||
|
||||
class GlobItem:
|
||||
def __init__(self, pattern):
|
||||
self.pattern = pattern
|
||||
|
||||
def __repr__(self):
|
||||
return self.pattern
|
||||
|
||||
def __eq__(self, other):
|
||||
if not isinstance(other, Command):
|
||||
return False
|
||||
|
||||
return (self.pattern == other.pattern)
|
||||
|
||||
def resolve(self, cwd):
|
||||
import glob
|
||||
import os
|
||||
if os.path.isabs(self.pattern):
|
||||
abspath = self.pattern
|
||||
else:
|
||||
abspath = os.path.join(cwd, self.pattern)
|
||||
results = glob.glob(abspath)
|
||||
return [self.pattern] if len(results) == 0 else results
|
||||
|
||||
class Pipeline:
|
||||
def __init__(self, commands, negate=False, pipe_err=False):
|
||||
self.commands = commands
|
||||
self.negate = negate
|
||||
self.pipe_err = pipe_err
|
||||
|
||||
def __repr__(self):
|
||||
return 'Pipeline(%r, %r, %r)' % (self.commands, self.negate,
|
||||
self.pipe_err)
|
||||
|
||||
def __eq__(self, other):
|
||||
if not isinstance(other, Pipeline):
|
||||
return False
|
||||
|
||||
return ((self.commands, self.negate, self.pipe_err) ==
|
||||
(other.commands, other.negate, self.pipe_err))
|
||||
|
||||
def toShell(self, file, pipefail=False):
|
||||
if pipefail != self.pipe_err:
|
||||
raise ValueError('Inconsistent "pipefail" attribute!')
|
||||
if self.negate:
|
||||
file.write('! ')
|
||||
for cmd in self.commands:
|
||||
cmd.toShell(file)
|
||||
if cmd is not self.commands[-1]:
|
||||
file.write('|\n ')
|
||||
|
||||
class Seq:
|
||||
def __init__(self, lhs, op, rhs):
|
||||
assert op in (';', '&', '||', '&&')
|
||||
self.op = op
|
||||
self.lhs = lhs
|
||||
self.rhs = rhs
|
||||
|
||||
def __repr__(self):
|
||||
return 'Seq(%r, %r, %r)' % (self.lhs, self.op, self.rhs)
|
||||
|
||||
def __eq__(self, other):
|
||||
if not isinstance(other, Seq):
|
||||
return False
|
||||
|
||||
return ((self.lhs, self.op, self.rhs) ==
|
||||
(other.lhs, other.op, other.rhs))
|
||||
|
||||
def toShell(self, file, pipefail=False):
|
||||
self.lhs.toShell(file, pipefail)
|
||||
file.write(' %s\n' % self.op)
|
||||
self.rhs.toShell(file, pipefail)
|
265
external/llvm/utils/lit/lit/ShUtil.py
vendored
265
external/llvm/utils/lit/lit/ShUtil.py
vendored
@ -1,265 +0,0 @@
|
||||
from __future__ import absolute_import
|
||||
import itertools
|
||||
|
||||
import lit.util
|
||||
from lit.ShCommands import Command, GlobItem, Pipeline, Seq
|
||||
|
||||
class ShLexer:
|
||||
def __init__(self, data, win32Escapes = False):
|
||||
self.data = data
|
||||
self.pos = 0
|
||||
self.end = len(data)
|
||||
self.win32Escapes = win32Escapes
|
||||
|
||||
def eat(self):
|
||||
c = self.data[self.pos]
|
||||
self.pos += 1
|
||||
return c
|
||||
|
||||
def look(self):
|
||||
return self.data[self.pos]
|
||||
|
||||
def maybe_eat(self, c):
|
||||
"""
|
||||
maybe_eat(c) - Consume the character c if it is the next character,
|
||||
returning True if a character was consumed. """
|
||||
if self.data[self.pos] == c:
|
||||
self.pos += 1
|
||||
return True
|
||||
return False
|
||||
|
||||
def lex_arg_fast(self, c):
|
||||
# Get the leading whitespace free section.
|
||||
chunk = self.data[self.pos - 1:].split(None, 1)[0]
|
||||
|
||||
# If it has special characters, the fast path failed.
|
||||
if ('|' in chunk or '&' in chunk or
|
||||
'<' in chunk or '>' in chunk or
|
||||
"'" in chunk or '"' in chunk or
|
||||
';' in chunk or '\\' in chunk):
|
||||
return None
|
||||
|
||||
self.pos = self.pos - 1 + len(chunk)
|
||||
return GlobItem(chunk) if '*' in chunk or '?' in chunk else chunk
|
||||
|
||||
def lex_arg_slow(self, c):
|
||||
if c in "'\"":
|
||||
str = self.lex_arg_quoted(c)
|
||||
else:
|
||||
str = c
|
||||
unquoted_glob_char = False
|
||||
quoted_glob_char = False
|
||||
while self.pos != self.end:
|
||||
c = self.look()
|
||||
if c.isspace() or c in "|&;":
|
||||
break
|
||||
elif c in '><':
|
||||
# This is an annoying case; we treat '2>' as a single token so
|
||||
# we don't have to track whitespace tokens.
|
||||
|
||||
# If the parse string isn't an integer, do the usual thing.
|
||||
if not str.isdigit():
|
||||
break
|
||||
|
||||
# Otherwise, lex the operator and convert to a redirection
|
||||
# token.
|
||||
num = int(str)
|
||||
tok = self.lex_one_token()
|
||||
assert isinstance(tok, tuple) and len(tok) == 1
|
||||
return (tok[0], num)
|
||||
elif c == '"' or c == "'":
|
||||
self.eat()
|
||||
quoted_arg = self.lex_arg_quoted(c)
|
||||
if '*' in quoted_arg or '?' in quoted_arg:
|
||||
quoted_glob_char = True
|
||||
str += quoted_arg
|
||||
elif not self.win32Escapes and c == '\\':
|
||||
# Outside of a string, '\\' escapes everything.
|
||||
self.eat()
|
||||
if self.pos == self.end:
|
||||
lit.util.warning(
|
||||
"escape at end of quoted argument in: %r" % self.data)
|
||||
return str
|
||||
str += self.eat()
|
||||
elif c in '*?':
|
||||
unquoted_glob_char = True
|
||||
str += self.eat()
|
||||
else:
|
||||
str += self.eat()
|
||||
# If a quote character is present, lex_arg_quoted will remove the quotes
|
||||
# and append the argument directly. This causes a problem when the
|
||||
# quoted portion contains a glob character, as the character will no
|
||||
# longer be treated literally. If glob characters occur *only* inside
|
||||
# of quotes, then we can handle this by not globbing at all, and if
|
||||
# glob characters occur *only* outside of quotes, we can still glob just
|
||||
# fine. But if a glob character occurs both inside and outside of
|
||||
# quotes this presents a problem. In practice this is such an obscure
|
||||
# edge case that it doesn't seem worth the added complexity to support.
|
||||
# By adding an assertion, it means some bot somewhere will catch this
|
||||
# and flag the user of a non-portable test (which could almost certainly
|
||||
# be re-written to work correctly without triggering this).
|
||||
assert not (quoted_glob_char and unquoted_glob_char)
|
||||
return GlobItem(str) if unquoted_glob_char else str
|
||||
|
||||
def lex_arg_quoted(self, delim):
|
||||
str = ''
|
||||
while self.pos != self.end:
|
||||
c = self.eat()
|
||||
if c == delim:
|
||||
return str
|
||||
elif c == '\\' and delim == '"':
|
||||
# Inside a '"' quoted string, '\\' only escapes the quote
|
||||
# character and backslash, otherwise it is preserved.
|
||||
if self.pos == self.end:
|
||||
lit.util.warning(
|
||||
"escape at end of quoted argument in: %r" % self.data)
|
||||
return str
|
||||
c = self.eat()
|
||||
if c == '"': #
|
||||
str += '"'
|
||||
elif c == '\\':
|
||||
str += '\\'
|
||||
else:
|
||||
str += '\\' + c
|
||||
else:
|
||||
str += c
|
||||
lit.util.warning("missing quote character in %r" % self.data)
|
||||
return str
|
||||
|
||||
def lex_arg_checked(self, c):
|
||||
pos = self.pos
|
||||
res = self.lex_arg_fast(c)
|
||||
end = self.pos
|
||||
|
||||
self.pos = pos
|
||||
reference = self.lex_arg_slow(c)
|
||||
if res is not None:
|
||||
if res != reference:
|
||||
raise ValueError("Fast path failure: %r != %r" % (
|
||||
res, reference))
|
||||
if self.pos != end:
|
||||
raise ValueError("Fast path failure: %r != %r" % (
|
||||
self.pos, end))
|
||||
return reference
|
||||
|
||||
def lex_arg(self, c):
|
||||
return self.lex_arg_fast(c) or self.lex_arg_slow(c)
|
||||
|
||||
def lex_one_token(self):
|
||||
"""
|
||||
lex_one_token - Lex a single 'sh' token. """
|
||||
|
||||
c = self.eat()
|
||||
if c == ';':
|
||||
return (c,)
|
||||
if c == '|':
|
||||
if self.maybe_eat('|'):
|
||||
return ('||',)
|
||||
return (c,)
|
||||
if c == '&':
|
||||
if self.maybe_eat('&'):
|
||||
return ('&&',)
|
||||
if self.maybe_eat('>'):
|
||||
return ('&>',)
|
||||
return (c,)
|
||||
if c == '>':
|
||||
if self.maybe_eat('&'):
|
||||
return ('>&',)
|
||||
if self.maybe_eat('>'):
|
||||
return ('>>',)
|
||||
return (c,)
|
||||
if c == '<':
|
||||
if self.maybe_eat('&'):
|
||||
return ('<&',)
|
||||
if self.maybe_eat('>'):
|
||||
return ('<<',)
|
||||
return (c,)
|
||||
|
||||
return self.lex_arg(c)
|
||||
|
||||
def lex(self):
|
||||
while self.pos != self.end:
|
||||
if self.look().isspace():
|
||||
self.eat()
|
||||
else:
|
||||
yield self.lex_one_token()
|
||||
|
||||
###
|
||||
|
||||
class ShParser:
|
||||
def __init__(self, data, win32Escapes = False, pipefail = False):
|
||||
self.data = data
|
||||
self.pipefail = pipefail
|
||||
self.tokens = ShLexer(data, win32Escapes = win32Escapes).lex()
|
||||
|
||||
def lex(self):
|
||||
for item in self.tokens:
|
||||
return item
|
||||
return None
|
||||
|
||||
def look(self):
|
||||
token = self.lex()
|
||||
if token is not None:
|
||||
self.tokens = itertools.chain([token], self.tokens)
|
||||
return token
|
||||
|
||||
def parse_command(self):
|
||||
tok = self.lex()
|
||||
if not tok:
|
||||
raise ValueError("empty command!")
|
||||
if isinstance(tok, tuple):
|
||||
raise ValueError("syntax error near unexpected token %r" % tok[0])
|
||||
|
||||
args = [tok]
|
||||
redirects = []
|
||||
while 1:
|
||||
tok = self.look()
|
||||
|
||||
# EOF?
|
||||
if tok is None:
|
||||
break
|
||||
|
||||
# If this is an argument, just add it to the current command.
|
||||
if isinstance(tok, (str, GlobItem)):
|
||||
args.append(self.lex())
|
||||
continue
|
||||
|
||||
# Otherwise see if it is a terminator.
|
||||
assert isinstance(tok, tuple)
|
||||
if tok[0] in ('|',';','&','||','&&'):
|
||||
break
|
||||
|
||||
# Otherwise it must be a redirection.
|
||||
op = self.lex()
|
||||
arg = self.lex()
|
||||
if not arg:
|
||||
raise ValueError("syntax error near token %r" % op[0])
|
||||
redirects.append((op, arg))
|
||||
|
||||
return Command(args, redirects)
|
||||
|
||||
def parse_pipeline(self):
|
||||
negate = False
|
||||
|
||||
commands = [self.parse_command()]
|
||||
while self.look() == ('|',):
|
||||
self.lex()
|
||||
commands.append(self.parse_command())
|
||||
return Pipeline(commands, negate, self.pipefail)
|
||||
|
||||
def parse(self):
|
||||
lhs = self.parse_pipeline()
|
||||
|
||||
while self.look():
|
||||
operator = self.lex()
|
||||
assert isinstance(operator, tuple) and len(operator) == 1
|
||||
|
||||
if not self.look():
|
||||
raise ValueError(
|
||||
"missing argument to operator %r" % operator[0])
|
||||
|
||||
# FIXME: Operator precedence!!
|
||||
lhs = Seq(lhs, operator[0], self.parse_pipeline())
|
||||
|
||||
return lhs
|
||||
|
362
external/llvm/utils/lit/lit/Test.py
vendored
362
external/llvm/utils/lit/lit/Test.py
vendored
@ -1,362 +0,0 @@
|
||||
import os
|
||||
from xml.sax.saxutils import escape
|
||||
from json import JSONEncoder
|
||||
|
||||
from lit.BooleanExpression import BooleanExpression
|
||||
|
||||
# Test result codes.
|
||||
|
||||
class ResultCode(object):
|
||||
"""Test result codes."""
|
||||
|
||||
# We override __new__ and __getnewargs__ to ensure that pickling still
|
||||
# provides unique ResultCode objects in any particular instance.
|
||||
_instances = {}
|
||||
def __new__(cls, name, isFailure):
|
||||
res = cls._instances.get(name)
|
||||
if res is None:
|
||||
cls._instances[name] = res = super(ResultCode, cls).__new__(cls)
|
||||
return res
|
||||
def __getnewargs__(self):
|
||||
return (self.name, self.isFailure)
|
||||
|
||||
def __init__(self, name, isFailure):
|
||||
self.name = name
|
||||
self.isFailure = isFailure
|
||||
|
||||
def __repr__(self):
|
||||
return '%s%r' % (self.__class__.__name__,
|
||||
(self.name, self.isFailure))
|
||||
|
||||
PASS = ResultCode('PASS', False)
|
||||
FLAKYPASS = ResultCode('FLAKYPASS', False)
|
||||
XFAIL = ResultCode('XFAIL', False)
|
||||
FAIL = ResultCode('FAIL', True)
|
||||
XPASS = ResultCode('XPASS', True)
|
||||
UNRESOLVED = ResultCode('UNRESOLVED', True)
|
||||
UNSUPPORTED = ResultCode('UNSUPPORTED', False)
|
||||
TIMEOUT = ResultCode('TIMEOUT', True)
|
||||
|
||||
# Test metric values.
|
||||
|
||||
class MetricValue(object):
|
||||
def format(self):
|
||||
"""
|
||||
format() -> str
|
||||
|
||||
Convert this metric to a string suitable for displaying as part of the
|
||||
console output.
|
||||
"""
|
||||
raise RuntimeError("abstract method")
|
||||
|
||||
def todata(self):
|
||||
"""
|
||||
todata() -> json-serializable data
|
||||
|
||||
Convert this metric to content suitable for serializing in the JSON test
|
||||
output.
|
||||
"""
|
||||
raise RuntimeError("abstract method")
|
||||
|
||||
class IntMetricValue(MetricValue):
|
||||
def __init__(self, value):
|
||||
self.value = value
|
||||
|
||||
def format(self):
|
||||
return str(self.value)
|
||||
|
||||
def todata(self):
|
||||
return self.value
|
||||
|
||||
class RealMetricValue(MetricValue):
|
||||
def __init__(self, value):
|
||||
self.value = value
|
||||
|
||||
def format(self):
|
||||
return '%.4f' % self.value
|
||||
|
||||
def todata(self):
|
||||
return self.value
|
||||
|
||||
class JSONMetricValue(MetricValue):
|
||||
"""
|
||||
JSONMetricValue is used for types that are representable in the output
|
||||
but that are otherwise uninterpreted.
|
||||
"""
|
||||
def __init__(self, value):
|
||||
# Ensure the value is a serializable by trying to encode it.
|
||||
# WARNING: The value may change before it is encoded again, and may
|
||||
# not be encodable after the change.
|
||||
try:
|
||||
e = JSONEncoder()
|
||||
e.encode(value)
|
||||
except TypeError:
|
||||
raise
|
||||
self.value = value
|
||||
|
||||
def format(self):
|
||||
e = JSONEncoder(indent=2, sort_keys=True)
|
||||
return e.encode(self.value)
|
||||
|
||||
def todata(self):
|
||||
return self.value
|
||||
|
||||
def toMetricValue(value):
|
||||
if isinstance(value, MetricValue):
|
||||
return value
|
||||
elif isinstance(value, int):
|
||||
return IntMetricValue(value)
|
||||
elif isinstance(value, float):
|
||||
return RealMetricValue(value)
|
||||
else:
|
||||
# 'long' is only present in python2
|
||||
try:
|
||||
if isinstance(value, long):
|
||||
return IntMetricValue(value)
|
||||
except NameError:
|
||||
pass
|
||||
|
||||
# Try to create a JSONMetricValue and let the constructor throw
|
||||
# if value is not a valid type.
|
||||
return JSONMetricValue(value)
|
||||
|
||||
|
||||
# Test results.
|
||||
|
||||
class Result(object):
|
||||
"""Wrapper for the results of executing an individual test."""
|
||||
|
||||
def __init__(self, code, output='', elapsed=None):
|
||||
# The result code.
|
||||
self.code = code
|
||||
# The test output.
|
||||
self.output = output
|
||||
# The wall timing to execute the test, if timing.
|
||||
self.elapsed = elapsed
|
||||
# The metrics reported by this test.
|
||||
self.metrics = {}
|
||||
|
||||
def addMetric(self, name, value):
|
||||
"""
|
||||
addMetric(name, value)
|
||||
|
||||
Attach a test metric to the test result, with the given name and list of
|
||||
values. It is an error to attempt to attach the metrics with the same
|
||||
name multiple times.
|
||||
|
||||
Each value must be an instance of a MetricValue subclass.
|
||||
"""
|
||||
if name in self.metrics:
|
||||
raise ValueError("result already includes metrics for %r" % (
|
||||
name,))
|
||||
if not isinstance(value, MetricValue):
|
||||
raise TypeError("unexpected metric value: %r" % (value,))
|
||||
self.metrics[name] = value
|
||||
|
||||
# Test classes.
|
||||
|
||||
class TestSuite:
|
||||
"""TestSuite - Information on a group of tests.
|
||||
|
||||
A test suite groups together a set of logically related tests.
|
||||
"""
|
||||
|
||||
def __init__(self, name, source_root, exec_root, config):
|
||||
self.name = name
|
||||
self.source_root = source_root
|
||||
self.exec_root = exec_root
|
||||
# The test suite configuration.
|
||||
self.config = config
|
||||
|
||||
def getSourcePath(self, components):
|
||||
return os.path.join(self.source_root, *components)
|
||||
|
||||
def getExecPath(self, components):
|
||||
return os.path.join(self.exec_root, *components)
|
||||
|
||||
class Test:
|
||||
"""Test - Information on a single test instance."""
|
||||
|
||||
def __init__(self, suite, path_in_suite, config, file_path = None):
|
||||
self.suite = suite
|
||||
self.path_in_suite = path_in_suite
|
||||
self.config = config
|
||||
self.file_path = file_path
|
||||
|
||||
# A list of conditions under which this test is expected to fail.
|
||||
# Each condition is a boolean expression of features and target
|
||||
# triple parts. These can optionally be provided by test format
|
||||
# handlers, and will be honored when the test result is supplied.
|
||||
self.xfails = []
|
||||
|
||||
# A list of conditions that must be satisfied before running the test.
|
||||
# Each condition is a boolean expression of features. All of them
|
||||
# must be True for the test to run.
|
||||
# FIXME should target triple parts count here too?
|
||||
self.requires = []
|
||||
|
||||
# A list of conditions that prevent execution of the test.
|
||||
# Each condition is a boolean expression of features and target
|
||||
# triple parts. All of them must be False for the test to run.
|
||||
self.unsupported = []
|
||||
|
||||
# The test result, once complete.
|
||||
self.result = None
|
||||
|
||||
def setResult(self, result):
|
||||
if self.result is not None:
|
||||
raise ValueError("test result already set")
|
||||
if not isinstance(result, Result):
|
||||
raise ValueError("unexpected result type")
|
||||
|
||||
self.result = result
|
||||
|
||||
# Apply the XFAIL handling to resolve the result exit code.
|
||||
try:
|
||||
if self.isExpectedToFail():
|
||||
if self.result.code == PASS:
|
||||
self.result.code = XPASS
|
||||
elif self.result.code == FAIL:
|
||||
self.result.code = XFAIL
|
||||
except ValueError as e:
|
||||
# Syntax error in an XFAIL line.
|
||||
self.result.code = UNRESOLVED
|
||||
self.result.output = str(e)
|
||||
|
||||
def getFullName(self):
|
||||
return self.suite.config.name + ' :: ' + '/'.join(self.path_in_suite)
|
||||
|
||||
def getFilePath(self):
|
||||
if self.file_path:
|
||||
return self.file_path
|
||||
return self.getSourcePath()
|
||||
|
||||
def getSourcePath(self):
|
||||
return self.suite.getSourcePath(self.path_in_suite)
|
||||
|
||||
def getExecPath(self):
|
||||
return self.suite.getExecPath(self.path_in_suite)
|
||||
|
||||
def isExpectedToFail(self):
|
||||
"""
|
||||
isExpectedToFail() -> bool
|
||||
|
||||
Check whether this test is expected to fail in the current
|
||||
configuration. This check relies on the test xfails property which by
|
||||
some test formats may not be computed until the test has first been
|
||||
executed.
|
||||
Throws ValueError if an XFAIL line has a syntax error.
|
||||
"""
|
||||
|
||||
features = self.config.available_features
|
||||
triple = getattr(self.suite.config, 'target_triple', "")
|
||||
|
||||
# Check if any of the xfails match an available feature or the target.
|
||||
for item in self.xfails:
|
||||
# If this is the wildcard, it always fails.
|
||||
if item == '*':
|
||||
return True
|
||||
|
||||
# If this is a True expression of features and target triple parts,
|
||||
# it fails.
|
||||
try:
|
||||
if BooleanExpression.evaluate(item, features, triple):
|
||||
return True
|
||||
except ValueError as e:
|
||||
raise ValueError('Error in XFAIL list:\n%s' % str(e))
|
||||
|
||||
return False
|
||||
|
||||
def isWithinFeatureLimits(self):
|
||||
"""
|
||||
isWithinFeatureLimits() -> bool
|
||||
|
||||
A test is within the feature limits set by run_only_tests if
|
||||
1. the test's requirements ARE satisfied by the available features
|
||||
2. the test's requirements ARE NOT satisfied after the limiting
|
||||
features are removed from the available features
|
||||
|
||||
Throws ValueError if a REQUIRES line has a syntax error.
|
||||
"""
|
||||
|
||||
if not self.config.limit_to_features:
|
||||
return True # No limits. Run it.
|
||||
|
||||
# Check the requirements as-is (#1)
|
||||
if self.getMissingRequiredFeatures():
|
||||
return False
|
||||
|
||||
# Check the requirements after removing the limiting features (#2)
|
||||
featuresMinusLimits = [f for f in self.config.available_features
|
||||
if not f in self.config.limit_to_features]
|
||||
if not self.getMissingRequiredFeaturesFromList(featuresMinusLimits):
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def getMissingRequiredFeaturesFromList(self, features):
|
||||
try:
|
||||
return [item for item in self.requires
|
||||
if not BooleanExpression.evaluate(item, features)]
|
||||
except ValueError as e:
|
||||
raise ValueError('Error in REQUIRES list:\n%s' % str(e))
|
||||
|
||||
def getMissingRequiredFeatures(self):
|
||||
"""
|
||||
getMissingRequiredFeatures() -> list of strings
|
||||
|
||||
Returns a list of features from REQUIRES that are not satisfied."
|
||||
Throws ValueError if a REQUIRES line has a syntax error.
|
||||
"""
|
||||
|
||||
features = self.config.available_features
|
||||
return self.getMissingRequiredFeaturesFromList(features)
|
||||
|
||||
def getUnsupportedFeatures(self):
|
||||
"""
|
||||
getUnsupportedFeatures() -> list of strings
|
||||
|
||||
Returns a list of features from UNSUPPORTED that are present
|
||||
in the test configuration's features or target triple.
|
||||
Throws ValueError if an UNSUPPORTED line has a syntax error.
|
||||
"""
|
||||
|
||||
features = self.config.available_features
|
||||
triple = getattr(self.suite.config, 'target_triple', "")
|
||||
|
||||
try:
|
||||
return [item for item in self.unsupported
|
||||
if BooleanExpression.evaluate(item, features, triple)]
|
||||
except ValueError as e:
|
||||
raise ValueError('Error in UNSUPPORTED list:\n%s' % str(e))
|
||||
|
||||
def isEarlyTest(self):
|
||||
"""
|
||||
isEarlyTest() -> bool
|
||||
|
||||
Check whether this test should be executed early in a particular run.
|
||||
This can be used for test suites with long running tests to maximize
|
||||
parallelism or where it is desirable to surface their failures early.
|
||||
"""
|
||||
return self.suite.config.is_early
|
||||
|
||||
def getJUnitXML(self):
|
||||
test_name = self.path_in_suite[-1]
|
||||
test_path = self.path_in_suite[:-1]
|
||||
safe_test_path = [x.replace(".","_") for x in test_path]
|
||||
safe_name = self.suite.name.replace(".","-")
|
||||
|
||||
if safe_test_path:
|
||||
class_name = safe_name + "." + "/".join(safe_test_path)
|
||||
else:
|
||||
class_name = safe_name + "." + safe_name
|
||||
|
||||
xml = "<testcase classname='" + class_name + "' name='" + \
|
||||
test_name + "'"
|
||||
xml += " time='%.2f'" % (self.result.elapsed,)
|
||||
if self.result.code.isFailure:
|
||||
xml += ">\n\t<failure >\n" + escape(self.result.output)
|
||||
xml += "\n\t</failure>\n</testcase>"
|
||||
else:
|
||||
xml += "/>"
|
||||
return xml
|
1387
external/llvm/utils/lit/lit/TestRunner.py
vendored
1387
external/llvm/utils/lit/lit/TestRunner.py
vendored
File diff suppressed because it is too large
Load Diff
154
external/llvm/utils/lit/lit/TestingConfig.py
vendored
154
external/llvm/utils/lit/lit/TestingConfig.py
vendored
@ -1,154 +0,0 @@
|
||||
import os
|
||||
import sys
|
||||
|
||||
|
||||
class TestingConfig:
|
||||
""""
|
||||
TestingConfig - Information on the tests inside a suite.
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
def fromdefaults(litConfig):
|
||||
"""
|
||||
fromdefaults(litConfig) -> TestingConfig
|
||||
|
||||
Create a TestingConfig object with default values.
|
||||
"""
|
||||
# Set the environment based on the command line arguments.
|
||||
environment = {
|
||||
'PATH' : os.pathsep.join(litConfig.path +
|
||||
[os.environ.get('PATH','')]),
|
||||
'LLVM_DISABLE_CRASH_REPORT' : '1',
|
||||
}
|
||||
|
||||
pass_vars = ['LIBRARY_PATH', 'LD_LIBRARY_PATH', 'SYSTEMROOT', 'TERM',
|
||||
'LD_PRELOAD', 'ASAN_OPTIONS', 'UBSAN_OPTIONS',
|
||||
'LSAN_OPTIONS', 'ADB', 'ANDROID_SERIAL',
|
||||
'SANITIZER_IGNORE_CVE_2016_2143', 'TMPDIR', 'TMP', 'TEMP',
|
||||
'TEMPDIR', 'AVRLIT_BOARD', 'AVRLIT_PORT']
|
||||
for var in pass_vars:
|
||||
val = os.environ.get(var, '')
|
||||
# Check for empty string as some variables such as LD_PRELOAD cannot be empty
|
||||
# ('') for OS's such as OpenBSD.
|
||||
if val:
|
||||
environment[var] = val
|
||||
|
||||
if sys.platform == 'win32':
|
||||
environment.update({
|
||||
'INCLUDE' : os.environ.get('INCLUDE',''),
|
||||
'PATHEXT' : os.environ.get('PATHEXT',''),
|
||||
'PYTHONUNBUFFERED' : '1',
|
||||
'TEMP' : os.environ.get('TEMP',''),
|
||||
'TMP' : os.environ.get('TMP',''),
|
||||
})
|
||||
|
||||
# Set the default available features based on the LitConfig.
|
||||
available_features = []
|
||||
if litConfig.useValgrind:
|
||||
available_features.append('valgrind')
|
||||
if litConfig.valgrindLeakCheck:
|
||||
available_features.append('vg_leak')
|
||||
|
||||
return TestingConfig(None,
|
||||
name = '<unnamed>',
|
||||
suffixes = set(),
|
||||
test_format = None,
|
||||
environment = environment,
|
||||
substitutions = [],
|
||||
unsupported = False,
|
||||
test_exec_root = None,
|
||||
test_source_root = None,
|
||||
excludes = [],
|
||||
available_features = available_features,
|
||||
pipefail = True)
|
||||
|
||||
def load_from_path(self, path, litConfig):
|
||||
"""
|
||||
load_from_path(path, litConfig)
|
||||
|
||||
Load the configuration module at the provided path into the given config
|
||||
object.
|
||||
"""
|
||||
|
||||
# Load the config script data.
|
||||
data = None
|
||||
f = open(path)
|
||||
try:
|
||||
data = f.read()
|
||||
except:
|
||||
litConfig.fatal('unable to load config file: %r' % (path,))
|
||||
f.close()
|
||||
|
||||
# Execute the config script to initialize the object.
|
||||
cfg_globals = dict(globals())
|
||||
cfg_globals['config'] = self
|
||||
cfg_globals['lit_config'] = litConfig
|
||||
cfg_globals['__file__'] = path
|
||||
try:
|
||||
exec(compile(data, path, 'exec'), cfg_globals, None)
|
||||
if litConfig.debug:
|
||||
litConfig.note('... loaded config %r' % path)
|
||||
except SystemExit:
|
||||
e = sys.exc_info()[1]
|
||||
# We allow normal system exit inside a config file to just
|
||||
# return control without error.
|
||||
if e.args:
|
||||
raise
|
||||
except:
|
||||
import traceback
|
||||
litConfig.fatal(
|
||||
'unable to parse config file %r, traceback: %s' % (
|
||||
path, traceback.format_exc()))
|
||||
|
||||
self.finish(litConfig)
|
||||
|
||||
def __init__(self, parent, name, suffixes, test_format,
|
||||
environment, substitutions, unsupported,
|
||||
test_exec_root, test_source_root, excludes,
|
||||
available_features, pipefail, limit_to_features = [],
|
||||
is_early = False, parallelism_group = ""):
|
||||
self.parent = parent
|
||||
self.name = str(name)
|
||||
self.suffixes = set(suffixes)
|
||||
self.test_format = test_format
|
||||
self.environment = dict(environment)
|
||||
self.substitutions = list(substitutions)
|
||||
self.unsupported = unsupported
|
||||
self.test_exec_root = test_exec_root
|
||||
self.test_source_root = test_source_root
|
||||
self.excludes = set(excludes)
|
||||
self.available_features = set(available_features)
|
||||
self.pipefail = pipefail
|
||||
# This list is used by TestRunner.py to restrict running only tests that
|
||||
# require one of the features in this list if this list is non-empty.
|
||||
# Configurations can set this list to restrict the set of tests to run.
|
||||
self.limit_to_features = set(limit_to_features)
|
||||
# Whether the suite should be tested early in a given run.
|
||||
self.is_early = bool(is_early)
|
||||
self.parallelism_group = parallelism_group
|
||||
|
||||
def finish(self, litConfig):
|
||||
"""finish() - Finish this config object, after loading is complete."""
|
||||
|
||||
self.name = str(self.name)
|
||||
self.suffixes = set(self.suffixes)
|
||||
self.environment = dict(self.environment)
|
||||
self.substitutions = list(self.substitutions)
|
||||
if self.test_exec_root is not None:
|
||||
# FIXME: This should really only be suite in test suite config
|
||||
# files. Should we distinguish them?
|
||||
self.test_exec_root = str(self.test_exec_root)
|
||||
if self.test_source_root is not None:
|
||||
# FIXME: This should really only be suite in test suite config
|
||||
# files. Should we distinguish them?
|
||||
self.test_source_root = str(self.test_source_root)
|
||||
self.excludes = set(self.excludes)
|
||||
|
||||
@property
|
||||
def root(self):
|
||||
"""root attribute - The root configuration for the test suite."""
|
||||
if self.parent is None:
|
||||
return self
|
||||
else:
|
||||
return self.parent.root
|
||||
|
10
external/llvm/utils/lit/lit/__init__.py
vendored
10
external/llvm/utils/lit/lit/__init__.py
vendored
@ -1,10 +0,0 @@
|
||||
"""'lit' Testing Tool"""
|
||||
|
||||
__author__ = 'Daniel Dunbar'
|
||||
__email__ = 'daniel@minormatter.com'
|
||||
__versioninfo__ = (0, 6, 0)
|
||||
__version__ = '.'.join(str(v) for v in __versioninfo__) + 'dev'
|
||||
|
||||
__all__ = []
|
||||
|
||||
from .main import main
|
276
external/llvm/utils/lit/lit/discovery.py
vendored
276
external/llvm/utils/lit/lit/discovery.py
vendored
@ -1,276 +0,0 @@
|
||||
"""
|
||||
Test discovery functions.
|
||||
"""
|
||||
|
||||
import copy
|
||||
import os
|
||||
import sys
|
||||
|
||||
import lit.run
|
||||
from lit.TestingConfig import TestingConfig
|
||||
from lit import LitConfig, Test
|
||||
|
||||
def chooseConfigFileFromDir(dir, config_names):
|
||||
for name in config_names:
|
||||
p = os.path.join(dir, name)
|
||||
if os.path.exists(p):
|
||||
return p
|
||||
return None
|
||||
|
||||
def dirContainsTestSuite(path, lit_config):
|
||||
cfgpath = chooseConfigFileFromDir(path, lit_config.site_config_names)
|
||||
if not cfgpath:
|
||||
cfgpath = chooseConfigFileFromDir(path, lit_config.config_names)
|
||||
return cfgpath
|
||||
|
||||
def getTestSuite(item, litConfig, cache):
|
||||
"""getTestSuite(item, litConfig, cache) -> (suite, relative_path)
|
||||
|
||||
Find the test suite containing @arg item.
|
||||
|
||||
@retval (None, ...) - Indicates no test suite contains @arg item.
|
||||
@retval (suite, relative_path) - The suite that @arg item is in, and its
|
||||
relative path inside that suite.
|
||||
"""
|
||||
def search1(path):
|
||||
# Check for a site config or a lit config.
|
||||
cfgpath = dirContainsTestSuite(path, litConfig)
|
||||
|
||||
# If we didn't find a config file, keep looking.
|
||||
if not cfgpath:
|
||||
parent,base = os.path.split(path)
|
||||
if parent == path:
|
||||
return (None, ())
|
||||
|
||||
ts, relative = search(parent)
|
||||
return (ts, relative + (base,))
|
||||
|
||||
# This is a private builtin parameter which can be used to perform
|
||||
# translation of configuration paths. Specifically, this parameter
|
||||
# can be set to a dictionary that the discovery process will consult
|
||||
# when it finds a configuration it is about to load. If the given
|
||||
# path is in the map, the value of that key is a path to the
|
||||
# configuration to load instead.
|
||||
config_map = litConfig.params.get('config_map')
|
||||
if config_map:
|
||||
cfgpath = os.path.realpath(cfgpath)
|
||||
cfgpath = os.path.normcase(cfgpath)
|
||||
target = config_map.get(cfgpath)
|
||||
if target:
|
||||
cfgpath = target
|
||||
|
||||
# We found a test suite, create a new config for it and load it.
|
||||
if litConfig.debug:
|
||||
litConfig.note('loading suite config %r' % cfgpath)
|
||||
|
||||
cfg = TestingConfig.fromdefaults(litConfig)
|
||||
cfg.load_from_path(cfgpath, litConfig)
|
||||
source_root = os.path.realpath(cfg.test_source_root or path)
|
||||
exec_root = os.path.realpath(cfg.test_exec_root or path)
|
||||
return Test.TestSuite(cfg.name, source_root, exec_root, cfg), ()
|
||||
|
||||
def search(path):
|
||||
# Check for an already instantiated test suite.
|
||||
real_path = os.path.realpath(path)
|
||||
res = cache.get(real_path)
|
||||
if res is None:
|
||||
cache[real_path] = res = search1(path)
|
||||
return res
|
||||
|
||||
# Canonicalize the path.
|
||||
item = os.path.normpath(os.path.join(os.getcwd(), item))
|
||||
|
||||
# Skip files and virtual components.
|
||||
components = []
|
||||
while not os.path.isdir(item):
|
||||
parent,base = os.path.split(item)
|
||||
if parent == item:
|
||||
return (None, ())
|
||||
components.append(base)
|
||||
item = parent
|
||||
components.reverse()
|
||||
|
||||
ts, relative = search(item)
|
||||
return ts, tuple(relative + tuple(components))
|
||||
|
||||
def getLocalConfig(ts, path_in_suite, litConfig, cache):
|
||||
def search1(path_in_suite):
|
||||
# Get the parent config.
|
||||
if not path_in_suite:
|
||||
parent = ts.config
|
||||
else:
|
||||
parent = search(path_in_suite[:-1])
|
||||
|
||||
# Check if there is a local configuration file.
|
||||
source_path = ts.getSourcePath(path_in_suite)
|
||||
cfgpath = chooseConfigFileFromDir(source_path, litConfig.local_config_names)
|
||||
|
||||
# If not, just reuse the parent config.
|
||||
if not cfgpath:
|
||||
return parent
|
||||
|
||||
# Otherwise, copy the current config and load the local configuration
|
||||
# file into it.
|
||||
config = copy.deepcopy(parent)
|
||||
if litConfig.debug:
|
||||
litConfig.note('loading local config %r' % cfgpath)
|
||||
config.load_from_path(cfgpath, litConfig)
|
||||
return config
|
||||
|
||||
def search(path_in_suite):
|
||||
key = (ts, path_in_suite)
|
||||
res = cache.get(key)
|
||||
if res is None:
|
||||
cache[key] = res = search1(path_in_suite)
|
||||
return res
|
||||
|
||||
return search(path_in_suite)
|
||||
|
||||
def getTests(path, litConfig, testSuiteCache, localConfigCache):
|
||||
# Find the test suite for this input and its relative path.
|
||||
ts,path_in_suite = getTestSuite(path, litConfig, testSuiteCache)
|
||||
if ts is None:
|
||||
litConfig.warning('unable to find test suite for %r' % path)
|
||||
return (),()
|
||||
|
||||
if litConfig.debug:
|
||||
litConfig.note('resolved input %r to %r::%r' % (path, ts.name,
|
||||
path_in_suite))
|
||||
|
||||
return ts, getTestsInSuite(ts, path_in_suite, litConfig,
|
||||
testSuiteCache, localConfigCache)
|
||||
|
||||
def getTestsInSuite(ts, path_in_suite, litConfig,
|
||||
testSuiteCache, localConfigCache):
|
||||
# Check that the source path exists (errors here are reported by the
|
||||
# caller).
|
||||
source_path = ts.getSourcePath(path_in_suite)
|
||||
if not os.path.exists(source_path):
|
||||
return
|
||||
|
||||
# Check if the user named a test directly.
|
||||
if not os.path.isdir(source_path):
|
||||
lc = getLocalConfig(ts, path_in_suite[:-1], litConfig, localConfigCache)
|
||||
yield Test.Test(ts, path_in_suite, lc)
|
||||
return
|
||||
|
||||
# Otherwise we have a directory to search for tests, start by getting the
|
||||
# local configuration.
|
||||
lc = getLocalConfig(ts, path_in_suite, litConfig, localConfigCache)
|
||||
|
||||
# Search for tests.
|
||||
if lc.test_format is not None:
|
||||
for res in lc.test_format.getTestsInDirectory(ts, path_in_suite,
|
||||
litConfig, lc):
|
||||
yield res
|
||||
|
||||
# Search subdirectories.
|
||||
for filename in os.listdir(source_path):
|
||||
# FIXME: This doesn't belong here?
|
||||
if filename in ('Output', '.svn', '.git') or filename in lc.excludes:
|
||||
continue
|
||||
|
||||
# Ignore non-directories.
|
||||
file_sourcepath = os.path.join(source_path, filename)
|
||||
if not os.path.isdir(file_sourcepath):
|
||||
continue
|
||||
|
||||
# Check for nested test suites, first in the execpath in case there is a
|
||||
# site configuration and then in the source path.
|
||||
subpath = path_in_suite + (filename,)
|
||||
file_execpath = ts.getExecPath(subpath)
|
||||
if dirContainsTestSuite(file_execpath, litConfig):
|
||||
sub_ts, subpath_in_suite = getTestSuite(file_execpath, litConfig,
|
||||
testSuiteCache)
|
||||
elif dirContainsTestSuite(file_sourcepath, litConfig):
|
||||
sub_ts, subpath_in_suite = getTestSuite(file_sourcepath, litConfig,
|
||||
testSuiteCache)
|
||||
else:
|
||||
sub_ts = None
|
||||
|
||||
# If the this directory recursively maps back to the current test suite,
|
||||
# disregard it (this can happen if the exec root is located inside the
|
||||
# current test suite, for example).
|
||||
if sub_ts is ts:
|
||||
continue
|
||||
|
||||
# Otherwise, load from the nested test suite, if present.
|
||||
if sub_ts is not None:
|
||||
subiter = getTestsInSuite(sub_ts, subpath_in_suite, litConfig,
|
||||
testSuiteCache, localConfigCache)
|
||||
else:
|
||||
subiter = getTestsInSuite(ts, subpath, litConfig, testSuiteCache,
|
||||
localConfigCache)
|
||||
|
||||
N = 0
|
||||
for res in subiter:
|
||||
N += 1
|
||||
yield res
|
||||
if sub_ts and not N:
|
||||
litConfig.warning('test suite %r contained no tests' % sub_ts.name)
|
||||
|
||||
def find_tests_for_inputs(lit_config, inputs):
|
||||
"""
|
||||
find_tests_for_inputs(lit_config, inputs) -> [Test]
|
||||
|
||||
Given a configuration object and a list of input specifiers, find all the
|
||||
tests to execute.
|
||||
"""
|
||||
|
||||
# Expand '@...' form in inputs.
|
||||
actual_inputs = []
|
||||
for input in inputs:
|
||||
if input.startswith('@'):
|
||||
f = open(input[1:])
|
||||
try:
|
||||
for ln in f:
|
||||
ln = ln.strip()
|
||||
if ln:
|
||||
actual_inputs.append(ln)
|
||||
finally:
|
||||
f.close()
|
||||
else:
|
||||
actual_inputs.append(input)
|
||||
|
||||
# Load the tests from the inputs.
|
||||
tests = []
|
||||
test_suite_cache = {}
|
||||
local_config_cache = {}
|
||||
for input in actual_inputs:
|
||||
prev = len(tests)
|
||||
tests.extend(getTests(input, lit_config,
|
||||
test_suite_cache, local_config_cache)[1])
|
||||
if prev == len(tests):
|
||||
lit_config.warning('input %r contained no tests' % input)
|
||||
|
||||
# If there were any errors during test discovery, exit now.
|
||||
if lit_config.numErrors:
|
||||
sys.stderr.write('%d errors, exiting.\n' % lit_config.numErrors)
|
||||
sys.exit(2)
|
||||
|
||||
return tests
|
||||
|
||||
def load_test_suite(inputs):
|
||||
import platform
|
||||
import unittest
|
||||
from lit.LitTestCase import LitTestCase
|
||||
|
||||
# Create the global config object.
|
||||
litConfig = LitConfig.LitConfig(progname = 'lit',
|
||||
path = [],
|
||||
quiet = False,
|
||||
useValgrind = False,
|
||||
valgrindLeakCheck = False,
|
||||
valgrindArgs = [],
|
||||
singleProcess=False,
|
||||
noExecute = False,
|
||||
debug = False,
|
||||
isWindows = (platform.system()=='Windows'),
|
||||
params = {})
|
||||
|
||||
# Perform test discovery.
|
||||
run = lit.run.Run(litConfig, find_tests_for_inputs(litConfig, inputs))
|
||||
|
||||
# Return a unittest test suite which just runs the tests in order.
|
||||
return unittest.TestSuite([LitTestCase(test, run)
|
||||
for test in run.tests])
|
@ -1,8 +0,0 @@
|
||||
from lit.formats.base import ( # noqa: F401
|
||||
TestFormat,
|
||||
FileBasedTest,
|
||||
OneCommandPerFileTest
|
||||
)
|
||||
|
||||
from lit.formats.googletest import GoogleTest # noqa: F401
|
||||
from lit.formats.shtest import ShTest # noqa: F401
|
117
external/llvm/utils/lit/lit/formats/base.py
vendored
117
external/llvm/utils/lit/lit/formats/base.py
vendored
@ -1,117 +0,0 @@
|
||||
from __future__ import absolute_import
|
||||
import os
|
||||
|
||||
import lit.Test
|
||||
import lit.util
|
||||
|
||||
class TestFormat(object):
|
||||
pass
|
||||
|
||||
###
|
||||
|
||||
class FileBasedTest(TestFormat):
|
||||
def getTestsInDirectory(self, testSuite, path_in_suite,
|
||||
litConfig, localConfig):
|
||||
source_path = testSuite.getSourcePath(path_in_suite)
|
||||
for filename in os.listdir(source_path):
|
||||
# Ignore dot files and excluded tests.
|
||||
if (filename.startswith('.') or
|
||||
filename in localConfig.excludes):
|
||||
continue
|
||||
|
||||
filepath = os.path.join(source_path, filename)
|
||||
if not os.path.isdir(filepath):
|
||||
base,ext = os.path.splitext(filename)
|
||||
if ext in localConfig.suffixes:
|
||||
yield lit.Test.Test(testSuite, path_in_suite + (filename,),
|
||||
localConfig)
|
||||
|
||||
###
|
||||
|
||||
import re
|
||||
import tempfile
|
||||
|
||||
class OneCommandPerFileTest(TestFormat):
|
||||
# FIXME: Refactor into generic test for running some command on a directory
|
||||
# of inputs.
|
||||
|
||||
def __init__(self, command, dir, recursive=False,
|
||||
pattern=".*", useTempInput=False):
|
||||
if isinstance(command, str):
|
||||
self.command = [command]
|
||||
else:
|
||||
self.command = list(command)
|
||||
if dir is not None:
|
||||
dir = str(dir)
|
||||
self.dir = dir
|
||||
self.recursive = bool(recursive)
|
||||
self.pattern = re.compile(pattern)
|
||||
self.useTempInput = useTempInput
|
||||
|
||||
def getTestsInDirectory(self, testSuite, path_in_suite,
|
||||
litConfig, localConfig):
|
||||
dir = self.dir
|
||||
if dir is None:
|
||||
dir = testSuite.getSourcePath(path_in_suite)
|
||||
|
||||
for dirname,subdirs,filenames in os.walk(dir):
|
||||
if not self.recursive:
|
||||
subdirs[:] = []
|
||||
|
||||
subdirs[:] = [d for d in subdirs
|
||||
if (d != '.svn' and
|
||||
d not in localConfig.excludes)]
|
||||
|
||||
for filename in filenames:
|
||||
if (filename.startswith('.') or
|
||||
not self.pattern.match(filename) or
|
||||
filename in localConfig.excludes):
|
||||
continue
|
||||
|
||||
path = os.path.join(dirname,filename)
|
||||
suffix = path[len(dir):]
|
||||
if suffix.startswith(os.sep):
|
||||
suffix = suffix[1:]
|
||||
test = lit.Test.Test(
|
||||
testSuite, path_in_suite + tuple(suffix.split(os.sep)),
|
||||
localConfig)
|
||||
# FIXME: Hack?
|
||||
test.source_path = path
|
||||
yield test
|
||||
|
||||
def createTempInput(self, tmp, test):
|
||||
raise NotImplementedError('This is an abstract method.')
|
||||
|
||||
def execute(self, test, litConfig):
|
||||
if test.config.unsupported:
|
||||
return (lit.Test.UNSUPPORTED, 'Test is unsupported')
|
||||
|
||||
cmd = list(self.command)
|
||||
|
||||
# If using temp input, create a temporary file and hand it to the
|
||||
# subclass.
|
||||
if self.useTempInput:
|
||||
tmp = tempfile.NamedTemporaryFile(suffix='.cpp')
|
||||
self.createTempInput(tmp, test)
|
||||
tmp.flush()
|
||||
cmd.append(tmp.name)
|
||||
elif hasattr(test, 'source_path'):
|
||||
cmd.append(test.source_path)
|
||||
else:
|
||||
cmd.append(test.getSourcePath())
|
||||
|
||||
out, err, exitCode = lit.util.executeCommand(cmd)
|
||||
|
||||
diags = out + err
|
||||
if not exitCode and not diags.strip():
|
||||
return lit.Test.PASS,''
|
||||
|
||||
# Try to include some useful information.
|
||||
report = """Command: %s\n""" % ' '.join(["'%s'" % a
|
||||
for a in cmd])
|
||||
if self.useTempInput:
|
||||
report += """Temporary File: %s\n""" % tmp.name
|
||||
report += "--\n%s--\n""" % open(tmp.name).read()
|
||||
report += """Output:\n--\n%s--""" % diags
|
||||
|
||||
return lit.Test.FAIL, report
|
145
external/llvm/utils/lit/lit/formats/googletest.py
vendored
145
external/llvm/utils/lit/lit/formats/googletest.py
vendored
@ -1,145 +0,0 @@
|
||||
from __future__ import absolute_import
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
import lit.Test
|
||||
import lit.TestRunner
|
||||
import lit.util
|
||||
from .base import TestFormat
|
||||
|
||||
kIsWindows = sys.platform in ['win32', 'cygwin']
|
||||
|
||||
class GoogleTest(TestFormat):
|
||||
def __init__(self, test_sub_dirs, test_suffix):
|
||||
self.test_sub_dirs = os.path.normcase(str(test_sub_dirs)).split(';')
|
||||
|
||||
# On Windows, assume tests will also end in '.exe'.
|
||||
exe_suffix = str(test_suffix)
|
||||
if kIsWindows:
|
||||
exe_suffix += '.exe'
|
||||
|
||||
# Also check for .py files for testing purposes.
|
||||
self.test_suffixes = {exe_suffix, test_suffix + '.py'}
|
||||
|
||||
def getGTestTests(self, path, litConfig, localConfig):
|
||||
"""getGTestTests(path) - [name]
|
||||
|
||||
Return the tests available in gtest executable.
|
||||
|
||||
Args:
|
||||
path: String path to a gtest executable
|
||||
litConfig: LitConfig instance
|
||||
localConfig: TestingConfig instance"""
|
||||
|
||||
list_test_cmd = self.maybeAddPythonToCmd([path, '--gtest_list_tests'])
|
||||
|
||||
try:
|
||||
output = subprocess.check_output(list_test_cmd,
|
||||
env=localConfig.environment)
|
||||
except subprocess.CalledProcessError as exc:
|
||||
litConfig.warning(
|
||||
"unable to discover google-tests in %r: %s. Process output: %s"
|
||||
% (path, sys.exc_info()[1], exc.output))
|
||||
raise StopIteration
|
||||
|
||||
nested_tests = []
|
||||
for ln in output.splitlines(False): # Don't keep newlines.
|
||||
ln = lit.util.to_string(ln)
|
||||
|
||||
if 'Running main() from gtest_main.cc' in ln:
|
||||
# Upstream googletest prints this to stdout prior to running
|
||||
# tests. LLVM removed that print statement in r61540, but we
|
||||
# handle it here in case upstream googletest is being used.
|
||||
continue
|
||||
|
||||
# The test name list includes trailing comments beginning with
|
||||
# a '#' on some lines, so skip those. We don't support test names
|
||||
# that use escaping to embed '#' into their name as the names come
|
||||
# from C++ class and method names where such things are hard and
|
||||
# uninteresting to support.
|
||||
ln = ln.split('#', 1)[0].rstrip()
|
||||
if not ln.lstrip():
|
||||
continue
|
||||
|
||||
index = 0
|
||||
while ln[index*2:index*2+2] == ' ':
|
||||
index += 1
|
||||
while len(nested_tests) > index:
|
||||
nested_tests.pop()
|
||||
|
||||
ln = ln[index*2:]
|
||||
if ln.endswith('.'):
|
||||
nested_tests.append(ln)
|
||||
elif any([name.startswith('DISABLED_')
|
||||
for name in nested_tests + [ln]]):
|
||||
# Gtest will internally skip these tests. No need to launch a
|
||||
# child process for it.
|
||||
continue
|
||||
else:
|
||||
yield ''.join(nested_tests) + ln
|
||||
|
||||
def getTestsInDirectory(self, testSuite, path_in_suite,
|
||||
litConfig, localConfig):
|
||||
source_path = testSuite.getSourcePath(path_in_suite)
|
||||
for subdir in self.test_sub_dirs:
|
||||
dir_path = os.path.join(source_path, subdir)
|
||||
if not os.path.isdir(dir_path):
|
||||
continue
|
||||
for fn in lit.util.listdir_files(dir_path,
|
||||
suffixes=self.test_suffixes):
|
||||
# Discover the tests in this executable.
|
||||
execpath = os.path.join(source_path, subdir, fn)
|
||||
testnames = self.getGTestTests(execpath, litConfig, localConfig)
|
||||
for testname in testnames:
|
||||
testPath = path_in_suite + (subdir, fn, testname)
|
||||
yield lit.Test.Test(testSuite, testPath, localConfig,
|
||||
file_path=execpath)
|
||||
|
||||
def execute(self, test, litConfig):
|
||||
testPath,testName = os.path.split(test.getSourcePath())
|
||||
while not os.path.exists(testPath):
|
||||
# Handle GTest parametrized and typed tests, whose name includes
|
||||
# some '/'s.
|
||||
testPath, namePrefix = os.path.split(testPath)
|
||||
testName = namePrefix + '/' + testName
|
||||
|
||||
cmd = [testPath, '--gtest_filter=' + testName]
|
||||
cmd = self.maybeAddPythonToCmd(cmd)
|
||||
if litConfig.useValgrind:
|
||||
cmd = litConfig.valgrindArgs + cmd
|
||||
|
||||
if litConfig.noExecute:
|
||||
return lit.Test.PASS, ''
|
||||
|
||||
try:
|
||||
out, err, exitCode = lit.util.executeCommand(
|
||||
cmd, env=test.config.environment,
|
||||
timeout=litConfig.maxIndividualTestTime)
|
||||
except lit.util.ExecuteCommandTimeoutException:
|
||||
return (lit.Test.TIMEOUT,
|
||||
'Reached timeout of {} seconds'.format(
|
||||
litConfig.maxIndividualTestTime)
|
||||
)
|
||||
|
||||
if exitCode:
|
||||
return lit.Test.FAIL, out + err
|
||||
|
||||
passing_test_line = '[ PASSED ] 1 test.'
|
||||
if passing_test_line not in out:
|
||||
msg = ('Unable to find %r in gtest output:\n\n%s%s' %
|
||||
(passing_test_line, out, err))
|
||||
return lit.Test.UNRESOLVED, msg
|
||||
|
||||
return lit.Test.PASS,''
|
||||
|
||||
def maybeAddPythonToCmd(self, cmd):
|
||||
"""Insert the python exe into the command if cmd[0] ends in .py
|
||||
|
||||
We cannot rely on the system to interpret shebang lines for us on
|
||||
Windows, so add the python executable to the command if this is a .py
|
||||
script.
|
||||
"""
|
||||
if cmd[0].endswith('.py'):
|
||||
return [sys.executable] + cmd
|
||||
return cmd
|
25
external/llvm/utils/lit/lit/formats/shtest.py
vendored
25
external/llvm/utils/lit/lit/formats/shtest.py
vendored
@ -1,25 +0,0 @@
|
||||
from __future__ import absolute_import
|
||||
|
||||
import lit.TestRunner
|
||||
import lit.util
|
||||
|
||||
from .base import FileBasedTest
|
||||
|
||||
|
||||
class ShTest(FileBasedTest):
|
||||
"""ShTest is a format with one file per test.
|
||||
|
||||
This is the primary format for regression tests as described in the LLVM
|
||||
testing guide:
|
||||
|
||||
http://llvm.org/docs/TestingGuide.html
|
||||
|
||||
The ShTest files contain some number of shell-like command pipelines, along
|
||||
with assertions about what should be in the output.
|
||||
"""
|
||||
def __init__(self, execute_external=False):
|
||||
self.execute_external = execute_external
|
||||
|
||||
def execute(self, test, litConfig):
|
||||
return lit.TestRunner.executeShTest(test, litConfig,
|
||||
self.execute_external)
|
9
external/llvm/utils/lit/lit/llvm/__init__.py
vendored
9
external/llvm/utils/lit/lit/llvm/__init__.py
vendored
@ -1,9 +0,0 @@
|
||||
from lit.llvm import config
|
||||
|
||||
llvm_config = None
|
||||
|
||||
|
||||
def initialize(lit_config, test_config):
|
||||
global llvm_config
|
||||
|
||||
llvm_config = config.LLVMConfig(lit_config, test_config)
|
473
external/llvm/utils/lit/lit/llvm/config.py
vendored
473
external/llvm/utils/lit/lit/llvm/config.py
vendored
@ -1,473 +0,0 @@
|
||||
import os
|
||||
import platform
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
import lit.util
|
||||
from lit.llvm.subst import FindTool
|
||||
from lit.llvm.subst import ToolSubst
|
||||
|
||||
|
||||
def binary_feature(on, feature, off_prefix):
|
||||
return feature if on else off_prefix + feature
|
||||
|
||||
|
||||
class LLVMConfig(object):
|
||||
|
||||
def __init__(self, lit_config, config):
|
||||
self.lit_config = lit_config
|
||||
self.config = config
|
||||
|
||||
features = config.available_features
|
||||
|
||||
self.use_lit_shell = False
|
||||
# Tweak PATH for Win32 to decide to use bash.exe or not.
|
||||
if sys.platform == 'win32':
|
||||
# For tests that require Windows to run.
|
||||
features.add('system-windows')
|
||||
|
||||
# Seek sane tools in directories and set to $PATH.
|
||||
path = self.lit_config.getToolsPath(config.lit_tools_dir,
|
||||
config.environment['PATH'],
|
||||
['cmp.exe', 'grep.exe', 'sed.exe'])
|
||||
if path is not None:
|
||||
self.with_environment('PATH', path, append_path=True)
|
||||
self.use_lit_shell = True
|
||||
|
||||
# Choose between lit's internal shell pipeline runner and a real shell. If
|
||||
# LIT_USE_INTERNAL_SHELL is in the environment, we use that as an override.
|
||||
lit_shell_env = os.environ.get('LIT_USE_INTERNAL_SHELL')
|
||||
if lit_shell_env:
|
||||
self.use_lit_shell = lit.util.pythonize_bool(lit_shell_env)
|
||||
|
||||
if not self.use_lit_shell:
|
||||
features.add('shell')
|
||||
|
||||
# Running on Darwin OS
|
||||
if platform.system() == 'Darwin':
|
||||
# FIXME: lld uses the first, other projects use the second.
|
||||
# We should standardize on the former.
|
||||
features.add('system-linker-mach-o')
|
||||
features.add('system-darwin')
|
||||
elif platform.system() == 'Windows':
|
||||
# For tests that require Windows to run.
|
||||
features.add('system-windows')
|
||||
elif platform.system() == "Linux":
|
||||
features.add('system-linux')
|
||||
|
||||
# Native compilation: host arch == default triple arch
|
||||
# Both of these values should probably be in every site config (e.g. as
|
||||
# part of the standard header. But currently they aren't)
|
||||
host_triple = getattr(config, 'host_triple', None)
|
||||
target_triple = getattr(config, 'target_triple', None)
|
||||
if host_triple and host_triple == target_triple:
|
||||
features.add('native')
|
||||
|
||||
# Sanitizers.
|
||||
sanitizers = getattr(config, 'llvm_use_sanitizer', '')
|
||||
sanitizers = frozenset(x.lower() for x in sanitizers.split(';'))
|
||||
features.add(binary_feature('address' in sanitizers, 'asan', 'not_'))
|
||||
features.add(binary_feature('memory' in sanitizers, 'msan', 'not_'))
|
||||
features.add(binary_feature(
|
||||
'undefined' in sanitizers, 'ubsan', 'not_'))
|
||||
|
||||
have_zlib = getattr(config, 'have_zlib', None)
|
||||
features.add(binary_feature(have_zlib, 'zlib', 'no'))
|
||||
|
||||
# Check if we should run long running tests.
|
||||
long_tests = lit_config.params.get('run_long_tests', None)
|
||||
if lit.util.pythonize_bool(long_tests):
|
||||
features.add('long_tests')
|
||||
|
||||
if target_triple:
|
||||
if re.match(r'^x86_64.*-apple', target_triple):
|
||||
host_cxx = getattr(config, 'host_cxx', None)
|
||||
if 'address' in sanitizers and self.get_clang_has_lsan(host_cxx, target_triple):
|
||||
self.with_environment(
|
||||
'ASAN_OPTIONS', 'detect_leaks=1', append_path=True)
|
||||
if re.match(r'^x86_64.*-linux', target_triple):
|
||||
features.add('x86_64-linux')
|
||||
if re.match(r'.*-win32$', target_triple):
|
||||
features.add('target-windows')
|
||||
|
||||
use_gmalloc = lit_config.params.get('use_gmalloc', None)
|
||||
if lit.util.pythonize_bool(use_gmalloc):
|
||||
# Allow use of an explicit path for gmalloc library.
|
||||
# Will default to '/usr/lib/libgmalloc.dylib' if not set.
|
||||
gmalloc_path_str = lit_config.params.get('gmalloc_path',
|
||||
'/usr/lib/libgmalloc.dylib')
|
||||
if gmalloc_path_str is not None:
|
||||
self.with_environment(
|
||||
'DYLD_INSERT_LIBRARIES', gmalloc_path_str)
|
||||
|
||||
breaking_checks = getattr(config, 'enable_abi_breaking_checks', None)
|
||||
if lit.util.pythonize_bool(breaking_checks):
|
||||
features.add('abi-breaking-checks')
|
||||
|
||||
def with_environment(self, variable, value, append_path=False):
|
||||
if append_path:
|
||||
# For paths, we should be able to take a list of them and process all
|
||||
# of them.
|
||||
paths_to_add = value
|
||||
if lit.util.is_string(paths_to_add):
|
||||
paths_to_add = [paths_to_add]
|
||||
|
||||
def norm(x):
|
||||
return os.path.normcase(os.path.normpath(x))
|
||||
|
||||
current_paths = self.config.environment.get(variable, None)
|
||||
if current_paths:
|
||||
current_paths = current_paths.split(os.path.pathsep)
|
||||
paths = [norm(p) for p in current_paths]
|
||||
else:
|
||||
paths = []
|
||||
|
||||
# If we are passed a list [a b c], then iterating this list forwards
|
||||
# and adding each to the beginning would result in b c a. So we
|
||||
# need to iterate in reverse to end up with the original ordering.
|
||||
for p in reversed(paths_to_add):
|
||||
# Move it to the front if it already exists, otherwise insert it at the
|
||||
# beginning.
|
||||
p = norm(p)
|
||||
try:
|
||||
paths.remove(p)
|
||||
except ValueError:
|
||||
pass
|
||||
paths = [p] + paths
|
||||
value = os.pathsep.join(paths)
|
||||
self.config.environment[variable] = value
|
||||
|
||||
def with_system_environment(self, variables, append_path=False):
|
||||
if lit.util.is_string(variables):
|
||||
variables = [variables]
|
||||
for v in variables:
|
||||
value = os.environ.get(v)
|
||||
if value:
|
||||
self.with_environment(v, value, append_path)
|
||||
|
||||
def clear_environment(self, variables):
|
||||
for name in variables:
|
||||
if name in self.config.environment:
|
||||
del self.config.environment[name]
|
||||
|
||||
def get_process_output(self, command):
|
||||
try:
|
||||
cmd = subprocess.Popen(
|
||||
command, stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE, env=self.config.environment)
|
||||
stdout, stderr = cmd.communicate()
|
||||
stdout = lit.util.to_string(stdout)
|
||||
stderr = lit.util.to_string(stderr)
|
||||
return (stdout, stderr)
|
||||
except OSError:
|
||||
self.lit_config.fatal('Could not run process %s' % command)
|
||||
|
||||
def feature_config(self, features):
|
||||
# Ask llvm-config about the specified feature.
|
||||
arguments = [x for (x, _) in features]
|
||||
config_path = os.path.join(self.config.llvm_tools_dir, 'llvm-config')
|
||||
|
||||
output, _ = self.get_process_output([config_path] + arguments)
|
||||
lines = output.split('\n')
|
||||
|
||||
for (feature_line, (_, patterns)) in zip(lines, features):
|
||||
# We should have either a callable or a dictionary. If it's a
|
||||
# dictionary, grep each key against the output and use the value if
|
||||
# it matches. If it's a callable, it does the entire translation.
|
||||
if callable(patterns):
|
||||
features_to_add = patterns(feature_line)
|
||||
self.config.available_features.update(features_to_add)
|
||||
else:
|
||||
for (re_pattern, feature) in patterns.items():
|
||||
if re.search(re_pattern, feature_line):
|
||||
self.config.available_features.add(feature)
|
||||
|
||||
# Note that when substituting %clang_cc1 also fill in the include directory of
|
||||
# the builtin headers. Those are part of even a freestanding environment, but
|
||||
# Clang relies on the driver to locate them.
|
||||
def get_clang_builtin_include_dir(self, clang):
|
||||
# FIXME: Rather than just getting the version, we should have clang print
|
||||
# out its resource dir here in an easy to scrape form.
|
||||
clang_dir, _ = self.get_process_output(
|
||||
[clang, '-print-file-name=include'])
|
||||
|
||||
if not clang_dir:
|
||||
self.lit_config.fatal(
|
||||
"Couldn't find the include dir for Clang ('%s')" % clang)
|
||||
|
||||
clang_dir = clang_dir.strip()
|
||||
if sys.platform in ['win32'] and not self.use_lit_shell:
|
||||
# Don't pass dosish path separator to msys bash.exe.
|
||||
clang_dir = clang_dir.replace('\\', '/')
|
||||
# Ensure the result is an ascii string, across Python2.5+ - Python3.
|
||||
return clang_dir
|
||||
|
||||
# On macOS, LSan is only supported on clang versions 5 and higher
|
||||
def get_clang_has_lsan(self, clang, triple):
|
||||
if not clang:
|
||||
self.lit_config.warning(
|
||||
'config.host_cxx is unset but test suite is configured to use sanitizers.')
|
||||
return False
|
||||
|
||||
clang_binary = clang.split()[0]
|
||||
version_string, _ = self.get_process_output(
|
||||
[clang_binary, '--version'])
|
||||
if not 'clang' in version_string:
|
||||
self.lit_config.warning(
|
||||
"compiler '%s' does not appear to be clang, " % clang_binary +
|
||||
'but test suite is configured to use sanitizers.')
|
||||
return False
|
||||
|
||||
if re.match(r'.*-linux', triple):
|
||||
return True
|
||||
|
||||
if re.match(r'^x86_64.*-apple', triple):
|
||||
version_regex = re.search(r'version ([0-9]+)\.([0-9]+).([0-9]+)', version_string)
|
||||
major_version_number = int(version_regex.group(1))
|
||||
minor_version_number = int(version_regex.group(2))
|
||||
patch_version_number = int(version_regex.group(3))
|
||||
if 'Apple LLVM' in version_string:
|
||||
# Apple LLVM doesn't yet support LSan
|
||||
return False
|
||||
else:
|
||||
return major_version_number >= 5
|
||||
|
||||
return False
|
||||
|
||||
def make_itanium_abi_triple(self, triple):
|
||||
m = re.match(r'(\w+)-(\w+)-(\w+)', triple)
|
||||
if not m:
|
||||
self.lit_config.fatal(
|
||||
"Could not turn '%s' into Itanium ABI triple" % triple)
|
||||
if m.group(3).lower() != 'win32':
|
||||
# All non-win32 triples use the Itanium ABI.
|
||||
return triple
|
||||
return m.group(1) + '-' + m.group(2) + '-mingw32'
|
||||
|
||||
def make_msabi_triple(self, triple):
|
||||
m = re.match(r'(\w+)-(\w+)-(\w+)', triple)
|
||||
if not m:
|
||||
self.lit_config.fatal(
|
||||
"Could not turn '%s' into MS ABI triple" % triple)
|
||||
isa = m.group(1).lower()
|
||||
vendor = m.group(2).lower()
|
||||
os = m.group(3).lower()
|
||||
if os == 'win32':
|
||||
# If the OS is win32, we're done.
|
||||
return triple
|
||||
if isa.startswith('x86') or isa == 'amd64' or re.match(r'i\d86', isa):
|
||||
# For x86 ISAs, adjust the OS.
|
||||
return isa + '-' + vendor + '-win32'
|
||||
# -win32 is not supported for non-x86 targets; use a default.
|
||||
return 'i686-pc-win32'
|
||||
|
||||
def add_tool_substitutions(self, tools, search_dirs=None):
|
||||
if not search_dirs:
|
||||
search_dirs = [self.config.llvm_tools_dir]
|
||||
|
||||
if lit.util.is_string(search_dirs):
|
||||
search_dirs = [search_dirs]
|
||||
|
||||
tools = [x if isinstance(x, ToolSubst) else ToolSubst(x)
|
||||
for x in tools]
|
||||
|
||||
search_dirs = os.pathsep.join(search_dirs)
|
||||
substitutions = []
|
||||
|
||||
for tool in tools:
|
||||
match = tool.resolve(self, search_dirs)
|
||||
|
||||
# Either no match occurred, or there was an unresolved match that
|
||||
# is ignored.
|
||||
if not match:
|
||||
continue
|
||||
|
||||
subst_key, tool_pipe, command = match
|
||||
|
||||
# An unresolved match occurred that can't be ignored. Fail without
|
||||
# adding any of the previously-discovered substitutions.
|
||||
if not command:
|
||||
return False
|
||||
|
||||
substitutions.append((subst_key, tool_pipe + command))
|
||||
|
||||
self.config.substitutions.extend(substitutions)
|
||||
return True
|
||||
|
||||
def use_default_substitutions(self):
|
||||
tool_patterns = [
|
||||
ToolSubst('FileCheck', unresolved='fatal'),
|
||||
# Handle these specially as they are strings searched for during testing.
|
||||
ToolSubst(r'\| \bcount\b', command=FindTool(
|
||||
'count'), verbatim=True, unresolved='fatal'),
|
||||
ToolSubst(r'\| \bnot\b', command=FindTool('not'), verbatim=True, unresolved='fatal')]
|
||||
|
||||
self.config.substitutions.append(('%python', sys.executable))
|
||||
self.add_tool_substitutions(
|
||||
tool_patterns, [self.config.llvm_tools_dir])
|
||||
|
||||
def use_llvm_tool(self, name, search_env=None, required=False, quiet=False):
|
||||
"""Find the executable program 'name', optionally using the specified
|
||||
environment variable as an override before searching the
|
||||
configuration's PATH."""
|
||||
# If the override is specified in the environment, use it without
|
||||
# validation.
|
||||
if search_env:
|
||||
tool = self.config.environment.get(search_env)
|
||||
if tool:
|
||||
return tool
|
||||
|
||||
# Otherwise look in the path.
|
||||
tool = lit.util.which(name, self.config.environment['PATH'])
|
||||
|
||||
if required and not tool:
|
||||
message = "couldn't find '{}' program".format(name)
|
||||
if search_env:
|
||||
message = message + \
|
||||
', try setting {} in your environment'.format(search_env)
|
||||
self.lit_config.fatal(message)
|
||||
|
||||
if tool:
|
||||
tool = os.path.normpath(tool)
|
||||
if not self.lit_config.quiet and not quiet:
|
||||
self.lit_config.note('using {}: {}'.format(name, tool))
|
||||
return tool
|
||||
|
||||
def use_clang(self, required=True):
|
||||
"""Configure the test suite to be able to invoke clang.
|
||||
|
||||
Sets up some environment variables important to clang, locates a
|
||||
just-built or installed clang, and add a set of standard
|
||||
substitutions useful to any test suite that makes use of clang.
|
||||
|
||||
"""
|
||||
# Clear some environment variables that might affect Clang.
|
||||
#
|
||||
# This first set of vars are read by Clang, but shouldn't affect tests
|
||||
# that aren't specifically looking for these features, or are required
|
||||
# simply to run the tests at all.
|
||||
#
|
||||
# FIXME: Should we have a tool that enforces this?
|
||||
|
||||
# safe_env_vars = ('TMPDIR', 'TEMP', 'TMP', 'USERPROFILE', 'PWD',
|
||||
# 'MACOSX_DEPLOYMENT_TARGET', 'IPHONEOS_DEPLOYMENT_TARGET',
|
||||
# 'VCINSTALLDIR', 'VC100COMNTOOLS', 'VC90COMNTOOLS',
|
||||
# 'VC80COMNTOOLS')
|
||||
possibly_dangerous_env_vars = ['COMPILER_PATH', 'RC_DEBUG_OPTIONS',
|
||||
'CINDEXTEST_PREAMBLE_FILE', 'LIBRARY_PATH',
|
||||
'CPATH', 'C_INCLUDE_PATH', 'CPLUS_INCLUDE_PATH',
|
||||
'OBJC_INCLUDE_PATH', 'OBJCPLUS_INCLUDE_PATH',
|
||||
'LIBCLANG_TIMING', 'LIBCLANG_OBJTRACKING',
|
||||
'LIBCLANG_LOGGING', 'LIBCLANG_BGPRIO_INDEX',
|
||||
'LIBCLANG_BGPRIO_EDIT', 'LIBCLANG_NOTHREADS',
|
||||
'LIBCLANG_RESOURCE_USAGE',
|
||||
'LIBCLANG_CODE_COMPLETION_LOGGING']
|
||||
# Clang/Win32 may refer to %INCLUDE%. vsvarsall.bat sets it.
|
||||
if platform.system() != 'Windows':
|
||||
possibly_dangerous_env_vars.append('INCLUDE')
|
||||
|
||||
self.clear_environment(possibly_dangerous_env_vars)
|
||||
|
||||
# Tweak the PATH to include the tools dir and the scripts dir.
|
||||
# Put Clang first to avoid LLVM from overriding out-of-tree clang builds.
|
||||
possible_paths = ['clang_tools_dir', 'llvm_tools_dir']
|
||||
paths = [getattr(self.config, pp) for pp in possible_paths
|
||||
if getattr(self.config, pp, None)]
|
||||
self.with_environment('PATH', paths, append_path=True)
|
||||
|
||||
paths = [self.config.llvm_shlib_dir, self.config.llvm_libs_dir]
|
||||
self.with_environment('LD_LIBRARY_PATH', paths, append_path=True)
|
||||
|
||||
# Discover the 'clang' and 'clangcc' to use.
|
||||
|
||||
self.config.clang = self.use_llvm_tool(
|
||||
'clang', search_env='CLANG', required=required)
|
||||
|
||||
self.config.substitutions.append(
|
||||
('%llvmshlibdir', self.config.llvm_shlib_dir))
|
||||
self.config.substitutions.append(
|
||||
('%pluginext', self.config.llvm_plugin_ext))
|
||||
|
||||
builtin_include_dir = self.get_clang_builtin_include_dir(self.config.clang)
|
||||
tool_substitutions = [
|
||||
ToolSubst('%clang', command=self.config.clang),
|
||||
ToolSubst('%clang_analyze_cc1', command='%clang_cc1', extra_args=['-analyze']),
|
||||
ToolSubst('%clang_cc1', command=self.config.clang, extra_args=['-cc1', '-internal-isystem', builtin_include_dir, '-nostdsysteminc']),
|
||||
ToolSubst('%clang_cpp', command=self.config.clang, extra_args=['--driver-mode=cpp']),
|
||||
ToolSubst('%clang_cl', command=self.config.clang, extra_args=['--driver-mode=cl']),
|
||||
ToolSubst('%clangxx', command=self.config.clang, extra_args=['--driver-mode=g++']),
|
||||
]
|
||||
self.add_tool_substitutions(tool_substitutions)
|
||||
|
||||
self.config.substitutions.append(('%itanium_abi_triple',
|
||||
self.make_itanium_abi_triple(self.config.target_triple)))
|
||||
self.config.substitutions.append(('%ms_abi_triple',
|
||||
self.make_msabi_triple(self.config.target_triple)))
|
||||
self.config.substitutions.append(
|
||||
('%resource_dir', builtin_include_dir))
|
||||
|
||||
# The host triple might not be set, at least if we're compiling clang from
|
||||
# an already installed llvm.
|
||||
if self.config.host_triple and self.config.host_triple != '@LLVM_HOST_TRIPLE@':
|
||||
self.config.substitutions.append(('%target_itanium_abi_host_triple',
|
||||
'--target=%s' % self.make_itanium_abi_triple(self.config.host_triple)))
|
||||
else:
|
||||
self.config.substitutions.append(
|
||||
('%target_itanium_abi_host_triple', ''))
|
||||
|
||||
self.config.substitutions.append(
|
||||
('%src_include_dir', self.config.clang_src_dir + '/include'))
|
||||
|
||||
# FIXME: Find nicer way to prohibit this.
|
||||
self.config.substitutions.append(
|
||||
(' clang ', """*** Do not use 'clang' in tests, use '%clang'. ***"""))
|
||||
self.config.substitutions.append(
|
||||
(' clang\+\+ ', """*** Do not use 'clang++' in tests, use '%clangxx'. ***"""))
|
||||
self.config.substitutions.append(
|
||||
(' clang-cc ',
|
||||
"""*** Do not use 'clang-cc' in tests, use '%clang_cc1'. ***"""))
|
||||
self.config.substitutions.append(
|
||||
(' clang -cc1 -analyze ',
|
||||
"""*** Do not use 'clang -cc1 -analyze' in tests, use '%clang_analyze_cc1'. ***"""))
|
||||
self.config.substitutions.append(
|
||||
(' clang -cc1 ',
|
||||
"""*** Do not use 'clang -cc1' in tests, use '%clang_cc1'. ***"""))
|
||||
self.config.substitutions.append(
|
||||
(' %clang-cc1 ',
|
||||
"""*** invalid substitution, use '%clang_cc1'. ***"""))
|
||||
self.config.substitutions.append(
|
||||
(' %clang-cpp ',
|
||||
"""*** invalid substitution, use '%clang_cpp'. ***"""))
|
||||
self.config.substitutions.append(
|
||||
(' %clang-cl ',
|
||||
"""*** invalid substitution, use '%clang_cl'. ***"""))
|
||||
|
||||
def use_lld(self, required=True):
|
||||
"""Configure the test suite to be able to invoke lld.
|
||||
|
||||
Sets up some environment variables important to lld, locates a
|
||||
just-built or installed lld, and add a set of standard
|
||||
substitutions useful to any test suite that makes use of lld.
|
||||
|
||||
"""
|
||||
# Tweak the PATH to include the tools dir
|
||||
tool_dirs = [self.config.llvm_tools_dir]
|
||||
lib_dirs = [self.config.llvm_libs_dir]
|
||||
lld_tools_dir = getattr(self.config, 'lld_tools_dir', None)
|
||||
lld_libs_dir = getattr(self.config, 'lld_libs_dir', None)
|
||||
|
||||
if lld_tools_dir:
|
||||
tool_dirs = tool_dirs + [lld_tools_dir]
|
||||
if lld_libs_dir:
|
||||
lib_dirs = lib_dirs + [lld_libs_dir]
|
||||
|
||||
self.with_environment('PATH', tool_dirs, append_path=True)
|
||||
self.with_environment('LD_LIBRARY_PATH', lib_dirs, append_path=True)
|
||||
|
||||
self.config.substitutions.append(
|
||||
(r"\bld.lld\b", 'ld.lld --full-shutdown'))
|
||||
|
||||
tool_patterns = ['ld.lld', 'lld-link', 'lld']
|
||||
|
||||
self.add_tool_substitutions(tool_patterns, tool_dirs)
|
145
external/llvm/utils/lit/lit/llvm/subst.py
vendored
145
external/llvm/utils/lit/lit/llvm/subst.py
vendored
@ -1,145 +0,0 @@
|
||||
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)
|
605
external/llvm/utils/lit/lit/main.py
vendored
605
external/llvm/utils/lit/lit/main.py
vendored
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user