Remove old test runner, this has moved to LLVM/utils/lit and all known clients

have been updated.
 - Please let me know of any problems.

llvm-svn: 82524
This commit is contained in:
Daniel Dunbar 2009-09-22 05:16:02 +00:00
parent f9539d0c3f
commit b5cbf77c2e
13 changed files with 140 additions and 1637 deletions

View File

@ -8,8 +8,8 @@ else
TESTDIRS ?= $(PROJ_SRC_DIR)
endif
# LIT2 wants objdir paths, so it will pick up the lit.site.cfg.
LIT2_TESTDIRS := $(TESTDIRS:$(PROJ_SRC_DIR)%=$(PROJ_OBJ_DIR)%)
# 'lit' wants objdir paths, so it will pick up the lit.site.cfg.
TESTDIRS := $(TESTDIRS:$(PROJ_SRC_DIR)%=$(PROJ_OBJ_DIR)%)
ifndef TESTARGS
ifdef VERBOSE
@ -25,20 +25,10 @@ else
VGARG=
endif
ifndef LIT1
all:: lit.site.cfg
@ echo '--- Running clang tests for $(TARGET_TRIPLE) ---'
@ $(LLVM_SRC_ROOT)/utils/lit/lit.py \
$(TESTARGS) $(LIT2_TESTDIRS) $(VGARG)
else
all::
@ echo '--- Running clang tests for $(TARGET_TRIPLE) ---'
@ $(PROJ_SRC_DIR)/../utils/test/MultiTestRunner.py \
--root $(PROJ_SRC_DIR) \
--path $(ToolDir) \
--path $(LLVM_SRC_ROOT)/test/Scripts \
$(TESTARGS) $(TESTDIRS) $(VGARG)
endif
FORCE:

View File

@ -1,166 +1,146 @@
# -*- Python -*-
def config_new():
import os
# Configuration file for the 'lit' test runner.
# name: The name of this test suite.
config.name = 'Clang'
# testFormat: The test format to use to interpret tests.
#
# For now we require '&&' between commands, until they get globally killed and
# the test runner updated.
config.test_format = lit.formats.ShTest(execute_external = True,
require_and_and = True)
# suffixes: A list of file extensions to treat as test files.
config.suffixes = ['.c', '.cpp', '.m', '.mm']
# test_source_root: The root path where tests are located.
config.test_source_root = os.path.dirname(__file__)
# test_exec_root: The root path where tests should be run.
clang_obj_root = getattr(config, 'clang_obj_root', None)
if clang_obj_root is not None:
config.test_exec_root = os.path.join(clang_obj_root, 'test')
# Set llvm_{src,obj}_root for use by others.
config.llvm_src_root = getattr(config, 'llvm_src_root', None)
config.llvm_obj_root = getattr(config, 'llvm_obj_root', None)
# Tweak the PATH to include the tools dir and the scripts dir.
if clang_obj_root is not None:
llvm_tools_dir = getattr(config, 'llvm_tools_dir', None)
if not llvm_tools_dir:
lit.fatal('No LLVM tools dir set!')
path = os.path.pathsep.join((llvm_tools_dir,
os.path.join(config.llvm_src_root,
'test', 'Scripts'),
config.environment['PATH']))
config.environment['PATH'] = path
###
# Check that the object root is known.
if config.test_exec_root is None:
# Otherwise, we haven't loaded the site specific configuration (the user is
# probably trying to run on a test file directly, and either the site
# configuration hasn't been created by the build system, or we are in an
# out-of-tree build situation).
# Try to detect the situation where we are using an out-of-tree build by
# looking for 'llvm-config'.
#
# FIXME: I debated (i.e., wrote and threw away) adding logic to
# automagically generate the lit.site.cfg if we are in some kind of fresh
# build situation. This means knowing how to invoke the build system
# though, and I decided it was too much magic.
llvm_config = lit.util.which('llvm-config', config.environment['PATH'])
if not llvm_config:
lit.fatal('No site specific configuration available!')
# Get the source and object roots.
llvm_src_root = lit.util.capture(['llvm-config', '--src-root']).strip()
llvm_obj_root = lit.util.capture(['llvm-config', '--obj-root']).strip()
clang_src_root = os.path.join(llvm_src_root, "tools", "clang")
clang_obj_root = os.path.join(llvm_obj_root, "tools", "clang")
# Validate that we got a tree which points to here, using the standard
# tools/clang layout.
this_src_root = os.path.dirname(config.test_source_root)
if os.path.realpath(clang_src_root) != os.path.realpath(this_src_root):
lit.fatal('No site specific configuration available!')
# Check that the site specific configuration exists.
site_cfg = os.path.join(clang_obj_root, 'test', 'lit.site.cfg')
if not os.path.exists(site_cfg):
lit.fatal('No site specific configuration available!')
# Okay, that worked. Notify the user of the automagic, and reconfigure.
lit.note('using out-of-tree build at %r' % clang_obj_root)
lit.load_config(config, site_cfg)
raise SystemExit
###
# Discover the 'clang' and 'clangcc' to use.
import os
def inferClang(PATH):
# Determine which clang to use.
clang = os.getenv('CLANG')
# If the user set clang in the environment, definitely use that and don't
# try to validate.
if clang:
return clang
# Otherwise look in the path.
clang = lit.util.which('clang', PATH)
if not clang:
lit.fatal("couldn't find 'clang' program, try setting "
"CLANG in your environment")
return clang
def inferClangCC(clang, PATH):
clangcc = os.getenv('CLANGCC')
# If the user set clang in the environment, definitely use that and don't
# try to validate.
if clangcc:
return clangcc
# Otherwise try adding -cc since we expect to be looking in a build
# directory.
if clang.endswith('.exe'):
clangccName = clang[:-4] + '-cc.exe'
else:
clangccName = clang + '-cc'
clangcc = lit.util.which(clangccName, PATH)
if not clangcc:
# Otherwise ask clang.
res = lit.util.capture([clang, '-print-prog-name=clang-cc'])
res = res.strip()
if res and os.path.exists(res):
clangcc = res
if not clangcc:
lit.fatal("couldn't find 'clang-cc' program, try setting "
"CLANGCC in your environment")
return clangcc
config.clang = inferClang(config.environment['PATH'])
if not lit.quiet:
lit.note('using clang: %r' % config.clang)
config.substitutions.append( (' clang ', ' ' + config.clang + ' ') )
config.clang_cc = inferClangCC(config.clang, config.environment['PATH'])
if not lit.quiet:
lit.note('using clang-cc: %r' % config.clang_cc)
config.substitutions.append( (' clang-cc ', ' ' + config.clang_cc + ' ') )
if 'config' in globals():
config_new()
raise SystemExit # End configuration.
import os
# Configuration file for the 'lit' test runner.
# suffixes: A list of file extensions to treat as test files.
suffixes = ['.c', '.cpp', '.m', '.mm']
# name: The name of this test suite.
config.name = 'Clang'
# environment: The base environment to use when running test commands.
# testFormat: The test format to use to interpret tests.
#
# The 'PATH' and 'SYSTEMROOT' variables will be set automatically from the lit
# command line variables.
environment = {}
# For now we require '&&' between commands, until they get globally killed and
# the test runner updated.
config.test_format = lit.formats.ShTest(execute_external = True,
require_and_and = True)
# requireAndAnd: Require '&&' between commands, until they get globally killed
# and the test runner updated.
requireAndAnd = True
# suffixes: A list of file extensions to treat as test files.
config.suffixes = ['.c', '.cpp', '.m', '.mm']
# test_source_root: The root path where tests are located.
config.test_source_root = os.path.dirname(__file__)
# test_exec_root: The root path where tests should be run.
clang_obj_root = getattr(config, 'clang_obj_root', None)
if clang_obj_root is not None:
config.test_exec_root = os.path.join(clang_obj_root, 'test')
# Set llvm_{src,obj}_root for use by others.
config.llvm_src_root = getattr(config, 'llvm_src_root', None)
config.llvm_obj_root = getattr(config, 'llvm_obj_root', None)
# Tweak the PATH to include the tools dir and the scripts dir.
if clang_obj_root is not None:
llvm_tools_dir = getattr(config, 'llvm_tools_dir', None)
if not llvm_tools_dir:
lit.fatal('No LLVM tools dir set!')
path = os.path.pathsep.join((llvm_tools_dir,
os.path.join(config.llvm_src_root,
'test', 'Scripts'),
config.environment['PATH']))
config.environment['PATH'] = path
###
# Check that the object root is known.
if config.test_exec_root is None:
# Otherwise, we haven't loaded the site specific configuration (the user is
# probably trying to run on a test file directly, and either the site
# configuration hasn't been created by the build system, or we are in an
# out-of-tree build situation).
# Try to detect the situation where we are using an out-of-tree build by
# looking for 'llvm-config'.
#
# FIXME: I debated (i.e., wrote and threw away) adding logic to
# automagically generate the lit.site.cfg if we are in some kind of fresh
# build situation. This means knowing how to invoke the build system
# though, and I decided it was too much magic.
llvm_config = lit.util.which('llvm-config', config.environment['PATH'])
if not llvm_config:
lit.fatal('No site specific configuration available!')
# Get the source and object roots.
llvm_src_root = lit.util.capture(['llvm-config', '--src-root']).strip()
llvm_obj_root = lit.util.capture(['llvm-config', '--obj-root']).strip()
clang_src_root = os.path.join(llvm_src_root, "tools", "clang")
clang_obj_root = os.path.join(llvm_obj_root, "tools", "clang")
# Validate that we got a tree which points to here, using the standard
# tools/clang layout.
this_src_root = os.path.dirname(config.test_source_root)
if os.path.realpath(clang_src_root) != os.path.realpath(this_src_root):
lit.fatal('No site specific configuration available!')
# Check that the site specific configuration exists.
site_cfg = os.path.join(clang_obj_root, 'test', 'lit.site.cfg')
if not os.path.exists(site_cfg):
lit.fatal('No site specific configuration available!')
# Okay, that worked. Notify the user of the automagic, and reconfigure.
lit.note('using out-of-tree build at %r' % clang_obj_root)
lit.load_config(config, site_cfg)
raise SystemExit
###
# Discover the 'clang' and 'clangcc' to use.
import os
def inferClang(PATH):
# Determine which clang to use.
clang = os.getenv('CLANG')
# If the user set clang in the environment, definitely use that and don't
# try to validate.
if clang:
return clang
# Otherwise look in the path.
clang = lit.util.which('clang', PATH)
if not clang:
lit.fatal("couldn't find 'clang' program, try setting "
"CLANG in your environment")
return clang
def inferClangCC(clang, PATH):
clangcc = os.getenv('CLANGCC')
# If the user set clang in the environment, definitely use that and don't
# try to validate.
if clangcc:
return clangcc
# Otherwise try adding -cc since we expect to be looking in a build
# directory.
if clang.endswith('.exe'):
clangccName = clang[:-4] + '-cc.exe'
else:
clangccName = clang + '-cc'
clangcc = lit.util.which(clangccName, PATH)
if not clangcc:
# Otherwise ask clang.
res = lit.util.capture([clang, '-print-prog-name=clang-cc'])
res = res.strip()
if res and os.path.exists(res):
clangcc = res
if not clangcc:
lit.fatal("couldn't find 'clang-cc' program, try setting "
"CLANGCC in your environment")
return clangcc
config.clang = inferClang(config.environment['PATH'])
if not lit.quiet:
lit.note('using clang: %r' % config.clang)
config.substitutions.append( (' clang ', ' ' + config.clang + ' ') )
config.clang_cc = inferClangCC(config.clang, config.environment['PATH'])
if not lit.quiet:
lit.note('using clang-cc: %r' % config.clang_cc)
config.substitutions.append( (' clang-cc ', ' ' + config.clang_cc + ' ') )

View File

@ -1,3 +0,0 @@
// RUN: echo 'I am some stdout' &&
// RUN: echo 'I am some stderr' 1>&2 &&
// RUN: false

View File

@ -1,12 +0,0 @@
# -*- Python -*-
# Configuration file for the 'lit' test runner.
# suffixes: A list of file extensions to treat as test files.
suffixes = ['.c', '.cpp', '.m', '.mm']
# environment: The base environment to use when running test commands.
#
# The 'PATH' and 'SYSTEMROOT' variables will be set automatically from the lit
# command line variables.
environment = {}

View File

@ -1 +0,0 @@
// RUN: true

View File

@ -1,2 +0,0 @@
// RUN: false
// XFAIL

View File

@ -1,2 +0,0 @@
// RUN: true
// XFAIL

View File

@ -1,399 +0,0 @@
#!/usr/bin/env python
"""
MultiTestRunner - Harness for running multiple tests in the simple clang style.
TODO
--
- Use configuration file for clang specific stuff
- Use a timeout / ulimit
- Detect signaled failures (abort)
- Better support for finding tests
- Support "disabling" tests? The advantage of making this distinct from XFAIL
is it makes it more obvious that it is a temporary measure (and MTR can put
in a separate category).
"""
import os, sys, re, random, time
import threading
from Queue import Queue
import ProgressBar
import TestRunner
import Util
from TestingConfig import TestingConfig
from TestRunner import TestStatus
kConfigName = 'lit.cfg'
def getTests(cfg, inputs):
for path in inputs:
if not os.path.exists(path):
Util.warning('Invalid test %r' % path)
continue
if not os.path.isdir(path):
yield path
continue
foundOne = False
for dirpath,dirnames,filenames in os.walk(path):
# FIXME: This doesn't belong here
if 'Output' in dirnames:
dirnames.remove('Output')
for f in filenames:
base,ext = os.path.splitext(f)
if ext in cfg.suffixes:
yield os.path.join(dirpath,f)
foundOne = True
if not foundOne:
Util.warning('No tests in input directory %r' % path)
class TestingProgressDisplay:
def __init__(self, opts, numTests, progressBar=None):
self.opts = opts
self.numTests = numTests
self.digits = len(str(self.numTests))
self.current = None
self.lock = threading.Lock()
self.progressBar = progressBar
self.progress = 0.
def update(self, index, tr):
# Avoid locking overhead in quiet mode
if self.opts.quiet and not tr.failed():
return
# Output lock
self.lock.acquire()
try:
self.handleUpdate(index, tr)
finally:
self.lock.release()
def finish(self):
if self.progressBar:
self.progressBar.clear()
elif self.opts.succinct:
sys.stdout.write('\n')
def handleUpdate(self, index, tr):
if self.progressBar:
if tr.failed():
self.progressBar.clear()
else:
# Force monotonicity
self.progress = max(self.progress, float(index)/self.numTests)
self.progressBar.update(self.progress, tr.path)
return
elif self.opts.succinct:
if not tr.failed():
sys.stdout.write('.')
sys.stdout.flush()
return
else:
sys.stdout.write('\n')
status = TestStatus.getName(tr.code).upper()
print '%s: %s (%*d of %*d)' % (status, tr.path,
self.digits, index+1,
self.digits, self.numTests)
if tr.failed() and self.opts.showOutput:
print "%s TEST '%s' FAILED %s" % ('*'*20, tr.path, '*'*20)
print tr.output
print "*" * 20
sys.stdout.flush()
class TestResult:
def __init__(self, path, code, output, elapsed):
self.path = path
self.code = code
self.output = output
self.elapsed = elapsed
def failed(self):
return self.code in (TestStatus.Fail,TestStatus.XPass)
class TestProvider:
def __init__(self, config, opts, tests, display):
self.config = config
self.opts = opts
self.tests = tests
self.index = 0
self.lock = threading.Lock()
self.results = [None]*len(self.tests)
self.startTime = time.time()
self.progress = display
def get(self):
self.lock.acquire()
try:
if self.opts.maxTime is not None:
if time.time() - self.startTime > self.opts.maxTime:
return None
if self.index >= len(self.tests):
return None
item = self.tests[self.index],self.index
self.index += 1
return item
finally:
self.lock.release()
def setResult(self, index, result):
self.results[index] = result
self.progress.update(index, result)
class Tester(threading.Thread):
def __init__(self, provider):
threading.Thread.__init__(self)
self.provider = provider
def run(self):
while 1:
item = self.provider.get()
if item is None:
break
self.runTest(item)
def runTest(self, (path, index)):
base = TestRunner.getTestOutputBase('Output', path)
numTests = len(self.provider.tests)
digits = len(str(numTests))
code = None
elapsed = None
try:
opts = self.provider.opts
startTime = time.time()
code, output = TestRunner.runOneTest(self.provider.config,
path, base)
elapsed = time.time() - startTime
except KeyboardInterrupt:
# This is a sad hack. Unfortunately subprocess goes
# bonkers with ctrl-c and we start forking merrily.
print '\nCtrl-C detected, goodbye.'
os.kill(0,9)
self.provider.setResult(index, TestResult(path, code, output, elapsed))
def findConfigPath(root):
prev = None
while root != prev:
cfg = os.path.join(root, kConfigName)
if os.path.exists(cfg):
return cfg
prev,root = root,os.path.dirname(root)
raise ValueError,"Unable to find config file %r" % kConfigName
def runTests(opts, provider):
# If only using one testing thread, don't use threads at all; this lets us
# profile, among other things.
if opts.numThreads == 1:
t = Tester(provider)
t.run()
return
# Otherwise spin up the testing threads and wait for them to finish.
testers = [Tester(provider) for i in range(opts.numThreads)]
for t in testers:
t.start()
try:
for t in testers:
t.join()
except KeyboardInterrupt:
sys.exit(1)
def main():
global options
from optparse import OptionParser, OptionGroup
parser = OptionParser("usage: %prog [options] {file-or-path}")
parser.add_option("", "--root", dest="root",
help="Path to root test directory",
action="store", default=None)
parser.add_option("", "--config", dest="config",
help="Testing configuration file [default='%s']" % kConfigName,
action="store", default=None)
group = OptionGroup(parser, "Output Format")
# FIXME: I find these names very confusing, although I like the
# functionality.
group.add_option("-q", "--quiet", dest="quiet",
help="Suppress no error output",
action="store_true", default=False)
group.add_option("-s", "--succinct", dest="succinct",
help="Reduce amount of output",
action="store_true", default=False)
group.add_option("-v", "--verbose", dest="showOutput",
help="Show all test output",
action="store_true", default=False)
group.add_option("", "--no-progress-bar", dest="useProgressBar",
help="Do not use curses based progress bar",
action="store_false", default=True)
parser.add_option_group(group)
group = OptionGroup(parser, "Test Execution")
group.add_option("-j", "--threads", dest="numThreads",
help="Number of testing threads",
type=int, action="store",
default=None)
group.add_option("", "--clang", dest="clang",
help="Program to use as \"clang\"",
action="store", default=None)
group.add_option("", "--clang-cc", dest="clangcc",
help="Program to use as \"clang-cc\"",
action="store", default=None)
group.add_option("", "--path", dest="path",
help="Additional paths to add to testing environment",
action="append", type=str, default=[])
group.add_option("", "--no-sh", dest="useExternalShell",
help="Run tests using an external shell",
action="store_false", default=True)
group.add_option("", "--vg", dest="useValgrind",
help="Run tests under valgrind",
action="store_true", default=False)
group.add_option("", "--vg-arg", dest="valgrindArgs",
help="Specify an extra argument for valgrind",
type=str, action="append", default=[])
group.add_option("", "--time-tests", dest="timeTests",
help="Track elapsed wall time for each test",
action="store_true", default=False)
parser.add_option_group(group)
group = OptionGroup(parser, "Test Selection")
group.add_option("", "--max-tests", dest="maxTests",
help="Maximum number of tests to run",
action="store", type=int, default=None)
group.add_option("", "--max-time", dest="maxTime",
help="Maximum time to spend testing (in seconds)",
action="store", type=float, default=None)
group.add_option("", "--shuffle", dest="shuffle",
help="Run tests in random order",
action="store_true", default=False)
parser.add_option_group(group)
(opts, args) = parser.parse_args()
if not args:
parser.error('No inputs specified')
if opts.numThreads is None:
opts.numThreads = Util.detectCPUs()
inputs = args
# Resolve root if not given, either infer it from the config file if given,
# otherwise from the inputs.
if not opts.root:
if opts.config:
opts.root = os.path.dirname(opts.config)
else:
opts.root = os.path.commonprefix([os.path.abspath(p)
for p in inputs])
# Find the config file, if not specified.
if not opts.config:
try:
opts.config = findConfigPath(opts.root)
except ValueError,e:
parser.error(e.args[0])
cfg = TestingConfig.frompath(opts.config)
# Update the configuration based on the command line arguments.
for name in ('PATH','SYSTEMROOT'):
if name in cfg.environment:
parser.error("'%s' should not be set in configuration!" % name)
cfg.root = opts.root
cfg.environment['PATH'] = os.pathsep.join(opts.path +
[os.environ.get('PATH','')])
cfg.environment['SYSTEMROOT'] = os.environ.get('SYSTEMROOT','')
if opts.clang is None:
opts.clang = TestRunner.inferClang(cfg)
if opts.clangcc is None:
opts.clangcc = TestRunner.inferClangCC(cfg, opts.clang)
cfg.clang = opts.clang
cfg.clangcc = opts.clangcc
cfg.useValgrind = opts.useValgrind
cfg.valgrindArgs = opts.valgrindArgs
cfg.useExternalShell = opts.useExternalShell
# FIXME: It could be worth loading these in parallel with testing.
allTests = list(getTests(cfg, args))
allTests.sort()
tests = allTests
if opts.shuffle:
random.shuffle(tests)
if opts.maxTests is not None:
tests = tests[:opts.maxTests]
extra = ''
if len(tests) != len(allTests):
extra = ' of %d'%(len(allTests),)
header = '-- Testing: %d%s tests, %d threads --'%(len(tests),extra,
opts.numThreads)
progressBar = None
if not opts.quiet:
if opts.useProgressBar:
try:
tc = ProgressBar.TerminalController()
progressBar = ProgressBar.ProgressBar(tc, header)
except ValueError:
pass
if not progressBar:
print header
# Don't create more threads than tests.
opts.numThreads = min(len(tests), opts.numThreads)
startTime = time.time()
display = TestingProgressDisplay(opts, len(tests), progressBar)
provider = TestProvider(cfg, opts, tests, display)
runTests(opts, provider)
display.finish()
if not opts.quiet:
print 'Testing Time: %.2fs'%(time.time() - startTime)
# List test results organized by kind.
byCode = {}
for t in provider.results:
if t:
if t.code not in byCode:
byCode[t.code] = []
byCode[t.code].append(t)
for title,code in (('Unexpected Passing Tests', TestStatus.XPass),
('Failing Tests', TestStatus.Fail)):
elts = byCode.get(code)
if not elts:
continue
print '*'*20
print '%s (%d):' % (title, len(elts))
for tr in elts:
print '\t%s'%(tr.path,)
numFailures = len(byCode.get(TestStatus.Fail,[]))
if numFailures:
print '\nFailures: %d' % (numFailures,)
sys.exit(1)
if opts.timeTests:
print '\nTest Times:'
provider.results.sort(key=lambda t: t and t.elapsed)
for tr in provider.results:
if tr:
print '%.2fs: %s' % (tr.elapsed, tr.path)
if __name__=='__main__':
main()

View File

@ -1,227 +0,0 @@
#!/usr/bin/env python
# Source: http://code.activestate.com/recipes/475116/, with
# modifications by Daniel Dunbar.
import sys, re, time
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')
# 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, curses.tparm(set_fg, i) or '')
set_fg_ansi = self._tigetstr('setaf')
if set_fg_ansi:
for i,color in zip(range(len(self._ANSICOLORS)), self._ANSICOLORS):
setattr(self, color, curses.tparm(set_fg_ansi, i) or '')
set_bg = self._tigetstr('setb')
if set_bg:
for i,color in zip(range(len(self._COLORS)), self._COLORS):
setattr(self, 'BG_'+color, curses.tparm(set_bg, i) or '')
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, curses.tparm(set_bg_ansi, i) 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) or ''
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 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\n'
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.width = self.term.COLS or 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.term.BOL + self.term.UP + self.term.CLEAR_EOL +
(self.bar % (prefix, '='*n, '-'*(barWidth-n), suffix)) +
self.term.CLEAR_EOL + message)
def clear(self):
if not self.cleared:
sys.stdout.write(self.term.BOL + self.term.CLEAR_EOL +
self.term.UP + self.term.CLEAR_EOL +
self.term.UP + self.term.CLEAR_EOL)
self.cleared = 1
def test():
import time
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,390 +0,0 @@
import itertools
import Util
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):
return None
self.pos = self.pos - 1 + len(chunk)
return chunk
def lex_arg_slow(self, c):
if c in "'\"":
str = self.lex_arg_quoted(c)
else:
str = c
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 == '"':
self.eat()
str += self.lex_arg_quoted('"')
elif not self.win32Escapes and c == '\\':
# Outside of a string, '\\' escapes everything.
self.eat()
if self.pos == self.end:
Util.warning("escape at end of quoted argument in: %r" %
self.data)
return str
str += self.eat()
else:
str += self.eat()
return 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:
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
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 in ';!':
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 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 __cmp__(self, other):
if not isinstance(other, Command):
return -1
return cmp((self.args, self.redirects),
(other.args, other.redirects))
class Pipeline:
def __init__(self, commands, negate):
self.commands = commands
self.negate = negate
def __repr__(self):
return 'Pipeline(%r, %r)' % (self.commands, self.negate)
def __cmp__(self, other):
if not isinstance(other, Pipeline):
return -1
return cmp((self.commands, self.negate),
(other.commands, other.negate))
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 __cmp__(self, other):
if not isinstance(other, Seq):
return -1
return cmp((self.lhs, self.op, self.rhs),
(other.lhs, other.op, other.rhs))
class ShParser:
def __init__(self, data, win32Escapes = False):
self.data = data
self.tokens = ShLexer(data, win32Escapes = win32Escapes).lex()
def lex(self):
try:
return self.tokens.next()
except StopIteration:
return None
def look(self):
next = self.lex()
if next is not None:
self.tokens = itertools.chain([next], self.tokens)
return next
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):
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
if self.look() == ('!',):
self.lex()
negate = True
commands = [self.parse_command()]
while self.look() == ('|',):
self.lex()
commands.append(self.parse_command())
return Pipeline(commands, negate)
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
###
import unittest
class TestShLexer(unittest.TestCase):
def lex(self, str, *args, **kwargs):
return list(ShLexer(str, *args, **kwargs).lex())
def test_basic(self):
self.assertEqual(self.lex('a|b>c&d<e'),
['a', ('|',), 'b', ('>',), 'c', ('&',), 'd',
('<',), 'e'])
def test_redirection_tokens(self):
self.assertEqual(self.lex('a2>c'),
['a2', ('>',), 'c'])
self.assertEqual(self.lex('a 2>c'),
['a', ('>',2), 'c'])
def test_quoting(self):
self.assertEqual(self.lex(""" 'a' """),
['a'])
self.assertEqual(self.lex(""" "hello\\"world" """),
['hello"world'])
self.assertEqual(self.lex(""" "hello\\'world" """),
["hello\\'world"])
self.assertEqual(self.lex(""" "hello\\\\world" """),
["hello\\world"])
self.assertEqual(self.lex(""" he"llo wo"rld """),
["hello world"])
self.assertEqual(self.lex(""" a\\ b a\\\\b """),
["a b", "a\\b"])
self.assertEqual(self.lex(""" "" "" """),
["", ""])
self.assertEqual(self.lex(""" a\\ b """, win32Escapes = True),
['a\\', 'b'])
class TestShParse(unittest.TestCase):
def parse(self, str):
return ShParser(str).parse()
def test_basic(self):
self.assertEqual(self.parse('echo hello'),
Pipeline([Command(['echo', 'hello'], [])], False))
self.assertEqual(self.parse('echo ""'),
Pipeline([Command(['echo', ''], [])], False))
def test_redirection(self):
self.assertEqual(self.parse('echo hello > c'),
Pipeline([Command(['echo', 'hello'],
[((('>'),), 'c')])], False))
self.assertEqual(self.parse('echo hello > c >> d'),
Pipeline([Command(['echo', 'hello'], [(('>',), 'c'),
(('>>',), 'd')])], False))
def test_pipeline(self):
self.assertEqual(self.parse('a | b'),
Pipeline([Command(['a'], []),
Command(['b'], [])],
False))
self.assertEqual(self.parse('a | b | c'),
Pipeline([Command(['a'], []),
Command(['b'], []),
Command(['c'], [])],
False))
self.assertEqual(self.parse('! a'),
Pipeline([Command(['a'], [])],
True))
def test_list(self):
self.assertEqual(self.parse('a ; b'),
Seq(Pipeline([Command(['a'], [])], False),
';',
Pipeline([Command(['b'], [])], False)))
self.assertEqual(self.parse('a & b'),
Seq(Pipeline([Command(['a'], [])], False),
'&',
Pipeline([Command(['b'], [])], False)))
self.assertEqual(self.parse('a && b'),
Seq(Pipeline([Command(['a'], [])], False),
'&&',
Pipeline([Command(['b'], [])], False)))
self.assertEqual(self.parse('a || b'),
Seq(Pipeline([Command(['a'], [])], False),
'||',
Pipeline([Command(['b'], [])], False)))
self.assertEqual(self.parse('a && b || c'),
Seq(Seq(Pipeline([Command(['a'], [])], False),
'&&',
Pipeline([Command(['b'], [])], False)),
'||',
Pipeline([Command(['c'], [])], False)))
if __name__ == '__main__':
unittest.main()

View File

@ -1,334 +0,0 @@
import os
import platform
import re
import signal
import subprocess
import sys
import ShUtil
import Util
kSystemName = platform.system()
class TestStatus:
Pass = 0
XFail = 1
Fail = 2
XPass = 3
Invalid = 4
kNames = ['Pass','XFail','Fail','XPass','Invalid']
@staticmethod
def getName(code):
return TestStatus.kNames[code]
def executeShCmd(cmd, cfg, cwd, results):
if isinstance(cmd, ShUtil.Seq):
if cmd.op == ';':
res = executeShCmd(cmd.lhs, cfg, cwd, results)
return executeShCmd(cmd.rhs, cfg, cwd, results)
if cmd.op == '&':
raise NotImplementedError,"unsupported test command: '&'"
if cmd.op == '||':
res = executeShCmd(cmd.lhs, cfg, cwd, results)
if res != 0:
res = executeShCmd(cmd.rhs, cfg, cwd, results)
return res
if cmd.op == '&&':
res = executeShCmd(cmd.lhs, cfg, cwd, results)
if res is None:
return res
if res == 0:
res = executeShCmd(cmd.rhs, cfg, cwd, results)
return res
raise ValueError,'Unknown shell command: %r' % cmd.op
assert isinstance(cmd, ShUtil.Pipeline)
procs = []
input = subprocess.PIPE
for j in cmd.commands:
# FIXME: This is broken, it doesn't account for the accumulative nature
# of redirects.
stdin = input
stdout = stderr = subprocess.PIPE
for r in j.redirects:
if r[0] == ('>',2):
stderr = open(r[1], 'w')
elif r[0] == ('>&',2) and r[1] == '1':
stderr = subprocess.STDOUT
elif r[0] == ('>',):
stdout = open(r[1], 'w')
elif r[0] == ('<',):
stdin = open(r[1], 'r')
else:
raise NotImplementedError,"Unsupported redirect: %r" % r
procs.append(subprocess.Popen(j.args, cwd=cwd,
stdin = stdin,
stdout = stdout,
stderr = stderr,
env = cfg.environment))
# Immediately close stdin for any process taking stdin from us.
if stdin == subprocess.PIPE:
procs[-1].stdin.close()
procs[-1].stdin = None
if stdout == subprocess.PIPE:
input = procs[-1].stdout
else:
input = subprocess.PIPE
# FIXME: There is a potential for deadlock here, when we have a pipe and
# some process other than the last one ends up blocked on stderr.
procData = [None] * len(procs)
procData[-1] = procs[-1].communicate()
for i in range(len(procs) - 1):
if procs[i].stdout is not None:
out = procs[i].stdout.read()
else:
out = ''
if procs[i].stderr is not None:
err = procs[i].stderr.read()
else:
err = ''
procData[i] = (out,err)
# FIXME: Fix tests to work with pipefail, and make exitCode max across
# procs.
for i,(out,err) in enumerate(procData):
exitCode = res = procs[i].wait()
results.append((cmd.commands[i], out, err, res))
if cmd.negate:
exitCode = not exitCode
return exitCode
def executeScriptInternal(cfg, commands, cwd):
cmd = ShUtil.ShParser(' &&\n'.join(commands),
kSystemName == 'Windows').parse()
results = []
try:
exitCode = executeShCmd(cmd, cfg, cwd, results)
except:
import traceback
out = ''
err = 'Exception during script execution:\n%s\n' % traceback.format_exc()
return out, err, 127
out = err = ''
for i,(cmd, cmd_out,cmd_err,res) in enumerate(results):
out += 'Command %d: %s\n' % (i, ' '.join('"%s"' % s for s in cmd.args))
out += 'Command %d Result: %r\n' % (i, res)
out += 'Command %d Output:\n%s\n\n' % (i, cmd_out)
out += 'Command %d Stderr:\n%s\n\n' % (i, cmd_err)
return out, err, exitCode
def executeScript(cfg, script, commands, cwd):
# Write script file
f = open(script,'w')
if kSystemName == 'Windows':
f.write('\nif %ERRORLEVEL% NEQ 0 EXIT\n'.join(commands))
else:
f.write(' &&\n'.join(commands))
f.write('\n')
f.close()
if kSystemName == 'Windows':
command = ['cmd','/c', script]
else:
command = ['/bin/sh', script]
if cfg.useValgrind:
# FIXME: Running valgrind on sh is overkill. We probably could just
# run on clang with no real loss.
valgrindArgs = ['valgrind', '-q',
'--tool=memcheck', '--trace-children=yes',
'--error-exitcode=123'] + cfg.valgrindArgs
command = valgrindArgs + command
p = subprocess.Popen(command, cwd=cwd,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
env=cfg.environment)
out,err = p.communicate()
exitCode = p.wait()
return out, err, exitCode
import StringIO
def runOneTest(cfg, testPath, tmpBase):
# Make paths absolute.
tmpBase = os.path.abspath(tmpBase)
testPath = os.path.abspath(testPath)
# Create the output directory if it does not already exist.
Util.mkdir_p(os.path.dirname(tmpBase))
script = tmpBase + '.script'
if kSystemName == 'Windows':
script += '.bat'
substitutions = [('%s', testPath),
('%S', os.path.dirname(testPath)),
('%t', tmpBase + '.tmp'),
(' clang ', ' ' + cfg.clang + ' '),
(' clang-cc ', ' ' + cfg.clangcc + ' ')]
# Collect the test lines from the script.
scriptLines = []
xfailLines = []
for ln in open(testPath):
if 'RUN:' in ln:
# Isolate the command to run.
index = ln.index('RUN:')
ln = ln[index+4:]
# Strip trailing newline.
scriptLines.append(ln)
elif 'XFAIL' in ln:
xfailLines.append(ln)
# FIXME: Support something like END, in case we need to process large
# files.
# Verify the script contains a run line.
if not scriptLines:
return (TestStatus.Fail, "Test has no run line!")
# Apply substitutions to the script.
def processLine(ln):
# Apply substitutions
for a,b in substitutions:
ln = ln.replace(a,b)
# Strip the trailing newline and any extra whitespace.
return ln.strip()
scriptLines = map(processLine, scriptLines)
# Validate interior lines for '&&', a lovely historical artifact.
for i in range(len(scriptLines) - 1):
ln = scriptLines[i]
if not ln.endswith('&&'):
return (TestStatus.Fail,
("MISSING \'&&\': %s\n" +
"FOLLOWED BY : %s\n") % (ln, scriptLines[i + 1]))
# Strip off '&&'
scriptLines[i] = ln[:-2]
if not cfg.useExternalShell:
res = executeScriptInternal(cfg, scriptLines, os.path.dirname(testPath))
if res is not None:
out, err, exitCode = res
elif True:
return (TestStatus.Fail,
"Unable to execute internally:\n%s\n"
% '\n'.join(scriptLines))
else:
out, err, exitCode = executeScript(cfg, script, scriptLines,
os.path.dirname(testPath))
else:
out, err, exitCode = executeScript(cfg, script, scriptLines,
os.path.dirname(testPath))
# Detect Ctrl-C in subprocess.
if exitCode == -signal.SIGINT:
raise KeyboardInterrupt
if xfailLines:
ok = exitCode != 0
status = (TestStatus.XPass, TestStatus.XFail)[ok]
else:
ok = exitCode == 0
status = (TestStatus.Fail, TestStatus.Pass)[ok]
if ok:
return (status,'')
output = StringIO.StringIO()
print >>output, "Script:"
print >>output, "--"
print >>output, '\n'.join(scriptLines)
print >>output, "--"
print >>output, "Exit Code: %r" % exitCode
print >>output, "Command Output (stdout):"
print >>output, "--"
output.write(out)
print >>output, "--"
print >>output, "Command Output (stderr):"
print >>output, "--"
output.write(err)
print >>output, "--"
return (status, output.getvalue())
def capture(args):
p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
out,_ = p.communicate()
return out
def inferClang(cfg):
# Determine which clang to use.
clang = os.getenv('CLANG')
# If the user set clang in the environment, definitely use that and don't
# try to validate.
if clang:
return clang
# Otherwise look in the path.
clang = Util.which('clang', cfg.environment['PATH'])
if not clang:
print >>sys.stderr, "error: couldn't find 'clang' program, try setting CLANG in your environment"
sys.exit(1)
return clang
def inferClangCC(cfg, clang):
clangcc = os.getenv('CLANGCC')
# If the user set clang in the environment, definitely use that and don't
# try to validate.
if clangcc:
return clangcc
# Otherwise try adding -cc since we expect to be looking in a build
# directory.
if clang.endswith('.exe'):
clangccName = clang[:-4] + '-cc.exe'
else:
clangccName = clang + '-cc'
clangcc = Util.which(clangccName, cfg.environment['PATH'])
if not clangcc:
# Otherwise ask clang.
res = capture([clang, '-print-prog-name=clang-cc'])
res = res.strip()
if res and os.path.exists(res):
clangcc = res
if not clangcc:
print >>sys.stderr, "error: couldn't find 'clang-cc' program, try setting CLANGCC in your environment"
sys.exit(1)
return clangcc
def getTestOutputBase(dir, testpath):
"""getTestOutputBase(dir, testpath) - Get the full path for temporary files
corresponding to the given test path."""
# Form the output base out of the test parent directory name and the test
# name. FIXME: Find a better way to organize test results.
return os.path.join(dir,
os.path.basename(os.path.dirname(testpath)),
os.path.basename(testpath))

View File

@ -1,27 +0,0 @@
class TestingConfig:
""""
TestingConfig - Information on a how to run a group of tests.
"""
@staticmethod
def frompath(path):
data = {}
f = open(path)
exec f in {},data
return TestingConfig(suffixes = data.get('suffixes', []),
environment = data.get('environment', {}))
def __init__(self, suffixes, environment):
self.suffixes = set(suffixes)
self.environment = dict(environment)
# Variables set internally.
self.root = None
self.useValgrind = None
self.useExternalShell = None
self.valgrindArgs = []
# FIXME: These need to move into a substitutions mechanism.
self.clang = None
self.clangcc = None

View File

@ -1,70 +0,0 @@
import errno, os, sys
def warning(msg):
print >>sys.stderr, '%s: warning: %s' % (sys.argv[0], msg)
def detectCPUs():
"""
Detects the number of CPUs on a system. Cribbed from pp.
"""
# Linux, Unix and MacOS:
if hasattr(os, "sysconf"):
if os.sysconf_names.has_key("SC_NPROCESSORS_ONLN"):
# Linux & Unix:
ncpus = os.sysconf("SC_NPROCESSORS_ONLN")
if isinstance(ncpus, int) and ncpus > 0:
return ncpus
else: # OSX:
return int(os.popen2("sysctl -n hw.ncpu")[1].read())
# Windows:
if os.environ.has_key("NUMBER_OF_PROCESSORS"):
ncpus = int(os.environ["NUMBER_OF_PROCESSORS"]);
if ncpus > 0:
return ncpus
return 1 # Default
def mkdir_p(path):
"""mkdir_p(path) - Make the "path" directory, if it does not exist; this
will also make directories for any missing parent directories."""
if not path or os.path.exists(path):
return
parent = os.path.dirname(path)
if parent != path:
mkdir_p(parent)
try:
os.mkdir(path)
except OSError,e:
# Ignore EEXIST, which may occur during a race condition.
if e.errno != errno.EEXIST:
raise
def which(command, paths = None):
"""which(command, [paths]) - Look up the given command in the paths string (or
the PATH environment variable, if unspecified)."""
if paths is None:
paths = os.environ.get('PATH','')
# Check for absolute match first.
if os.path.exists(command):
return command
# Would be nice if Python had a lib function for this.
if not paths:
paths = os.defpath
# Get suffixes to search.
pathext = os.environ.get('PATHEXT', '').split(os.pathsep)
# Search the paths...
for path in paths.split(os.pathsep):
for ext in pathext:
p = os.path.join(path, command + ext)
if os.path.exists(p):
return p
return None