Imported Upstream version 5.18.0.167

Former-commit-id: 289509151e0fee68a1b591a20c9f109c3c789d3a
This commit is contained in:
Xamarin Public Jenkins (auto-signing)
2018-10-20 08:25:10 +00:00
parent e19d552987
commit b084638f15
28489 changed files with 184 additions and 3866856 deletions

View File

@ -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()

View File

@ -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'))

View File

@ -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)

View File

@ -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)

View File

@ -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()

View File

@ -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)

View File

@ -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

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -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

View File

@ -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

View File

@ -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])

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

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