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
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:
# -*- Python -*-
-def config_new():
- import os
+import os
- # Configuration file for the 'lit' test runner.
+# Configuration file for the 'lit' test runner.
- # name: The name of this test suite.
- config.name = 'Clang'
+# 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")
+# 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
- 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
+ # Otherwise look in the path.
+ clang = lit.util.which('clang', PATH)
- config.clang = inferClang(config.environment['PATH'])
- if not lit.quiet:
- lit.note('using clang: %r' % config.clang)
- config.substitutions.append( (' clang ', ' ' + config.clang + ' ') )
+ if not clang:
+ lit.fatal("couldn't find 'clang' program, try setting "
+ "CLANG in your environment")
- 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 + ' ') )
+ return clang
-if 'config' in globals():
- config_new()
- raise SystemExit # End configuration.
+def inferClangCC(clang, PATH):
+ clangcc = os.getenv('CLANGCC')
-# 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 = {}
+ # If the user set clang in the environment, definitely use that and don't
+ # try to validate.
+ if clangcc:
+ return clangcc
-# requireAndAnd: Require '&&' between commands, until they get globally killed
-# and the test runner updated.
-requireAndAnd = True
+ # 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 + ' ') )
+++ /dev/null
-// RUN: echo 'I am some stdout' &&
-// RUN: echo 'I am some stderr' 1>&2 &&
-// RUN: false
+++ /dev/null
-# -*- 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 = {}
+++ /dev/null
-// RUN: true
+++ /dev/null
-// RUN: false
-// XFAIL
+++ /dev/null
-// RUN: true
-// XFAIL
+++ /dev/null
-#!/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()
+++ /dev/null
-#!/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()
+++ /dev/null
-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('"')\r
- 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()
+++ /dev/null
-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))
+++ /dev/null
-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
+++ /dev/null
-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
-