| Index: pydir/crosstest_generator.py
 | 
| diff --git a/pydir/crosstest_generator.py b/pydir/crosstest_generator.py
 | 
| new file mode 100755
 | 
| index 0000000000000000000000000000000000000000..3038f4ced26899801e3df2d95de39956877c85c6
 | 
| --- /dev/null
 | 
| +++ b/pydir/crosstest_generator.py
 | 
| @@ -0,0 +1,165 @@
 | 
| +#!/usr/bin/env python2
 | 
| +
 | 
| +import argparse
 | 
| +import ConfigParser
 | 
| +import os
 | 
| +import shutil
 | 
| +import sys
 | 
| +
 | 
| +from utils import shellcmd
 | 
| +from utils import FindBaseNaCl
 | 
| +
 | 
| +def Match(desc, includes, excludes, default_match):
 | 
| +  """Determines whether desc is a match against includes and excludes.
 | 
| +
 | 
| +  'desc' is a set of attributes, and 'includes' and 'excludes' are lists of sets
 | 
| +  of attributes.
 | 
| +
 | 
| +  If 'desc' matches any element from 'excludes', the result is False.
 | 
| +  Otherwise, if 'desc' matches any element from 'includes', the result is True.
 | 
| +  Otherwise, the 'default_match' value is returned.
 | 
| +  """
 | 
| +  for exclude in excludes:
 | 
| +    if exclude < desc:
 | 
| +      return False
 | 
| +  for include in includes:
 | 
| +    if include < desc:
 | 
| +      return True
 | 
| +  return default_match
 | 
| +
 | 
| +def main():
 | 
| +  """Framework for cross test generation and execution.
 | 
| +
 | 
| +  Builds and executes cross tests from the space of all possible attribute
 | 
| +  combinations.  The space can be restricted by providing subsets of attributes
 | 
| +  to specifically include or exclude.
 | 
| +  """
 | 
| +  # pypath is where to find other Subzero python scripts.
 | 
| +  pypath = os.path.abspath(os.path.dirname(sys.argv[0]))
 | 
| +  root = FindBaseNaCl()
 | 
| +
 | 
| +  # The rest of the attribute sets.
 | 
| +  targets = [ 'x8632' ]
 | 
| +  sandboxing = [ 'native', 'sandbox' ]
 | 
| +  opt_levels = [ 'Om1', 'O2' ]
 | 
| +  arch_attrs = [ 'sse2', 'sse4.1' ]
 | 
| +  # all_keys is only used in the help text.
 | 
| +  all_keys = '; '.join([' '.join(targets), ' '.join(sandboxing),
 | 
| +                        ' '.join(opt_levels), ' '.join(arch_attrs)])
 | 
| +
 | 
| +  argparser = argparse.ArgumentParser(
 | 
| +    description='  ' + main.__doc__ +
 | 
| +    'The set of attributes is the set of tests plus the following:\n' +
 | 
| +    all_keys, formatter_class=argparse.RawTextHelpFormatter)
 | 
| +  argparser.add_argument('--config', default='crosstest.cfg', dest='config',
 | 
| +                         metavar='FILE', help='Test configuration file')
 | 
| +  argparser.add_argument('--print-tests', default=False, action='store_true',
 | 
| +                         help='Print the set of test names and exit')
 | 
| +  argparser.add_argument('--include', '-i', default=[], dest='include',
 | 
| +                         action='append', metavar='ATTR_LIST',
 | 
| +                         help='Attributes to include (comma-separated). ' +
 | 
| +                              'Can be used multiple times.')
 | 
| +  argparser.add_argument('--exclude', '-e', default=[], dest='exclude',
 | 
| +                         action='append', metavar='ATTR_LIST',
 | 
| +                         help='Attributes to include (comma-separated). ' +
 | 
| +                              'Can be used multiple times.')
 | 
| +  argparser.add_argument('--verbose', '-v', default=False, action='store_true',
 | 
| +                         help='Use verbose output')
 | 
| +  argparser.add_argument('--defer', default=False, action='store_true',
 | 
| +                         help='Defer execution until all executables are built')
 | 
| +  argparser.add_argument('--no-compile', '-n', default=False,
 | 
| +                         action='store_true',
 | 
| +                         help="Don't build; reuse binaries from the last run")
 | 
| +  argparser.add_argument('--dir', dest='dir', metavar='DIRECTORY',
 | 
| +                         default=('{root}/toolchain_build/src/subzero/' +
 | 
| +                                  'crosstest/Output').format(root=root),
 | 
| +                         help='Output directory')
 | 
| +  argparser.add_argument('--lit', default=False, action='store_true',
 | 
| +                         help='Generate files for lit testing')
 | 
| +  args = argparser.parse_args()
 | 
| +
 | 
| +  # Run from the crosstest directory to make it easy to grab inputs.
 | 
| +  crosstest_dir = '{root}/toolchain_build/src/subzero/crosstest'.format(
 | 
| +    root=root)
 | 
| +  os.chdir(crosstest_dir)
 | 
| +
 | 
| +  tests = ConfigParser.RawConfigParser()
 | 
| +  tests.read('crosstest.cfg')
 | 
| +
 | 
| +  if args.print_tests:
 | 
| +    print 'Test name attributes: ' + ' '.join(sorted(tests.sections()))
 | 
| +    sys.exit(0)
 | 
| +
 | 
| +  # includes and excludes are both lists of sets.
 | 
| +  includes = [ set(item.split(',')) for item in args.include ]
 | 
| +  excludes = [ set(item.split(',')) for item in args.exclude ]
 | 
| +  # If any --include args are provided, the default is to not match.
 | 
| +  default_match = not args.include
 | 
| +
 | 
| +  # Delete and recreate the output directory, unless --no-compile was specified.
 | 
| +  if not args.no_compile:
 | 
| +    if os.path.exists(args.dir):
 | 
| +      if os.path.isdir(args.dir):
 | 
| +        shutil.rmtree(args.dir)
 | 
| +      else:
 | 
| +        os.remove(args.dir)
 | 
| +    if not os.path.exists(args.dir):
 | 
| +      os.makedirs(args.dir)
 | 
| +
 | 
| +  # If --defer is specified, collect the run commands into deferred_cmds for
 | 
| +  # later execution.
 | 
| +  deferred_cmds = []
 | 
| +  for test in sorted(tests.sections()):
 | 
| +    for target in targets:
 | 
| +      for sb in sandboxing:
 | 
| +        for opt in opt_levels:
 | 
| +          for attr in arch_attrs:
 | 
| +            desc = [ test, target, sb, opt, attr ]
 | 
| +            if Match(set(desc), includes, excludes, default_match):
 | 
| +              exe = '{test}_{target}_{sb}_{opt}_{attr}'.format(
 | 
| +                test=test, target=target, sb=sb, opt=opt,
 | 
| +                attr=attr)
 | 
| +              extra = (tests.get(test, 'flags').split(' ')
 | 
| +                       if tests.has_option(test, 'flags') else [])
 | 
| +              # Generate the compile command.
 | 
| +              cmp_cmd = ['{path}/crosstest.py'.format(path=pypath),
 | 
| +                         '-{opt}'.format(opt=opt),
 | 
| +                         '--mattr={attr}'.format(attr=attr),
 | 
| +                         '--prefix=Subzero_',
 | 
| +                         '--target={target}'.format(target=target),
 | 
| +                         '--sandbox={sb}'.format(sb='1' if sb=='sandbox'
 | 
| +                                                 else '0'),
 | 
| +                         '--dir={dir}'.format(dir=args.dir),
 | 
| +                         '--output={exe}'.format(exe=exe),
 | 
| +                         '--driver={drv}'.format(drv=tests.get(test, 'driver')),
 | 
| +                        ] + extra + \
 | 
| +                        [ '--test=' + t
 | 
| +                          for t in tests.get(test, 'test').split(' ') ]
 | 
| +              run_cmd_base = os.path.join(args.dir, exe)
 | 
| +              # Generate the run command.
 | 
| +              run_cmd = run_cmd_base
 | 
| +              if sb == 'sandbox':
 | 
| +                run_cmd = '{root}/run.py -q '.format(root=root) + run_cmd
 | 
| +              if args.lit:
 | 
| +                # Create a file to drive the lit test.
 | 
| +                with open(run_cmd_base + '.xtest', 'w') as f:
 | 
| +                  f.write('# RUN: sh %s | FileCheck %s\n')
 | 
| +                  f.write('cd ' + crosstest_dir + ' && \\\n')
 | 
| +                  f.write(' '.join(cmp_cmd) + ' && \\\n')
 | 
| +                  f.write(run_cmd + '\n')
 | 
| +                  f.write('echo Recreate a failure using ' + __file__ +
 | 
| +                          ' --include=' + ','.join(desc) + '\n')
 | 
| +                  f.write('# CHECK: Failures=0\n')
 | 
| +              else:
 | 
| +                if not args.no_compile:
 | 
| +                  shellcmd(cmp_cmd,
 | 
| +                           echo=args.verbose)
 | 
| +                if (args.defer):
 | 
| +                  deferred_cmds.append(run_cmd)
 | 
| +                else:
 | 
| +                  shellcmd(run_cmd, echo=True)
 | 
| +  for run_cmd in deferred_cmds:
 | 
| +    shellcmd(run_cmd, echo=True)
 | 
| +
 | 
| +if __name__ == '__main__':
 | 
| +  main()
 | 
| 
 |