--- /dev/null
+#!/usr/bin/env python
+##########################################################################
+#
+# Copyright 2011 VMware, Inc.
+# All Rights Reserved.
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the 'Software'), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+#
+##########################################################################/
+
+'''Check a trace replays successfully or not.
+
+It is meant to be used with git bisect. See git bisect manpage for more
+details.
+'''
+
+
+import optparse
+import os.path
+import platform
+import re
+import subprocess
+import sys
+import traceback
+
+
+def good():
+ '''Tell git-bisect that this commit is good.'''
+
+ sys.stdout.write('GOOD\n')
+ sys.exit(0)
+
+
+def bad():
+ '''Tell git-bisect that this commit is bad.'''
+
+ sys.stdout.write('BAD\n')
+ sys.exit(1)
+
+
+def skip():
+ '''Tell git-bisect to skip this commit.'''
+
+ sys.stdout.write('SKIP\n')
+ sys.exit(125)
+
+
+def abort():
+ '''Tell git-bisect to abort.'''
+
+ sys.stdout.write('ABORT\n')
+ sys.exit(-1)
+
+
+def which(executable):
+ '''Search for the executable on the PATH.'''
+
+ if platform.system() == 'Windows':
+ exts = ['.exe']
+ else:
+ exts = ['']
+ dirs = os.environ['PATH'].split(os.path.pathsep)
+ for dir in dirs:
+ path = os.path.join(dir, executable)
+ for ext in exts:
+ if os.path.exists(path + ext):
+ return True
+ return False
+
+
+def main():
+ '''Main program.
+
+ It will always invoke sys.exit(), and never return normally.
+ '''
+
+ # Try to guess the build command.
+ if os.path.exists('SConstruct'):
+ default_build = 'scons'
+ elif os.path.exists('Makefile'):
+ default_build = 'make'
+ else:
+ default_build = None
+
+ # Parse command line options
+ optparser = optparse.OptionParser(
+ usage='\n\tgit bisect run %prog [options] -- [glretrace options] <trace>',
+ version='%%prog')
+ optparser.add_option(
+ '-b', '--build', metavar='COMMAND',
+ type='string', dest='build', default=default_build,
+ help='build command [default: %default]')
+ optparser.add_option(
+ '-r', '--retrace', metavar='PROGRAM',
+ type='string', dest='retrace', default='glretrace',
+ help='retrace command [default: %default]')
+ optparser.add_option(
+ '--gl-renderer', metavar='REGEXP',
+ type='string', dest='gl_renderer_re', default='^.*$',
+ help='require a matching GL_RENDERER string [default: %default]')
+
+ (options, args) = optparser.parse_args(sys.argv[1:])
+ if not args:
+ optparser.error("incorrect number of arguments")
+
+ # Build the source
+ if options.build:
+ sys.stdout.write(options.build + '\n')
+ sys.stdout.flush()
+ returncode = subprocess.call(options.build, shell=True)
+ if returncode:
+ skip()
+
+ # TODO: For this to be useful on Windows we'll also need an installation
+ # procedure here.
+
+ # Do some sanity checks. In particular we want to make sure that the GL
+ # implementation is usable, and is the right one (i.e., we didn't fallback
+ # to a different OpenGL implementation due to missing symbols).
+ if platform.system() != 'Windows' and which('glxinfo'):
+ p = subprocess.Popen(['glxinfo'], stdout=subprocess.PIPE)
+ stdout, stderr = p.communicate()
+ if p.returncode:
+ skip()
+
+ # Search for the GL_RENDERER string
+ gl_renderer_header = 'OpenGL renderer string: '
+ gl_renderer = ''
+ for line in stdout.split('\n'):
+ if line.startswith(gl_renderer_header):
+ gl_renderer = line[len(gl_renderer_header):]
+
+ # and match it against the regular expression specified in the command
+ # line.
+ if not re.search(options.gl_renderer_re, gl_renderer):
+ sys.stderr.write("GL_RENDERER mismatch: %r !~ /%s/\n" % (gl_renderer, options.gl_renderer_re))
+ skip()
+
+ # Run glretrace
+ retrace = [options.retrace] + args
+ sys.stdout.write(' '.join(retrace) + '\n')
+ sys.stdout.flush()
+ returncode = subprocess.call(retrace)
+ if returncode:
+ # TODO: allow other criterias here, such as, snapshot comparison or performance threshold
+ bad()
+
+ # Success
+ good()
+
+
+# Invoke main program, aborting the bisection on Ctrl+C or any uncaught Python
+# exception.
+if __name__ == '__main__':
+ try:
+ main()
+ except SystemExit:
+ raise
+ except KeyboardInterrupt:
+ abort()
+ except:
+ traceback.print_exc()
+ abort()