1 | #!/usr/bin/env python
|
---|
2 | # use git bisect to work out what commit caused a test failure
|
---|
3 | # Copyright Andrew Tridgell 2010
|
---|
4 | # released under GNU GPL v3 or later
|
---|
5 |
|
---|
6 |
|
---|
7 | from subprocess import call, check_call, Popen, PIPE
|
---|
8 | import os, tempfile, sys
|
---|
9 | from optparse import OptionParser
|
---|
10 |
|
---|
11 | parser = OptionParser()
|
---|
12 | parser.add_option("", "--good", help="known good revision (default HEAD~100)", default='HEAD~100')
|
---|
13 | parser.add_option("", "--bad", help="known bad revision (default HEAD)", default='HEAD')
|
---|
14 | parser.add_option("", "--skip-build-errors", help="skip revision where make fails",
|
---|
15 | action='store_true', default=False)
|
---|
16 | parser.add_option("", "--autogen", help="run autogen before each build",action="store_true", default=False)
|
---|
17 | parser.add_option("", "--autogen-command", help="command to use for autogen (default ./autogen.sh)",
|
---|
18 | type='str', default="./autogen.sh")
|
---|
19 | parser.add_option("", "--configure", help="run configure.developer before each build",
|
---|
20 | action="store_true", default=False)
|
---|
21 | parser.add_option("", "--configure-command", help="the command for configure (default ./configure.developer)",
|
---|
22 | type='str', default="./configure.developer")
|
---|
23 | parser.add_option("", "--build-command", help="the command to build the tree (default 'make -j')",
|
---|
24 | type='str', default="make -j")
|
---|
25 | parser.add_option("", "--test-command", help="the command to test the tree (default 'make test')",
|
---|
26 | type='str', default="make test")
|
---|
27 | parser.add_option("", "--clean", help="run make clean before each build",
|
---|
28 | action="store_true", default=False)
|
---|
29 |
|
---|
30 |
|
---|
31 | (opts, args) = parser.parse_args()
|
---|
32 |
|
---|
33 |
|
---|
34 | def run_cmd(cmd, dir=".", show=True, output=False, checkfail=True):
|
---|
35 | if show:
|
---|
36 | print("Running: '%s' in '%s'" % (cmd, dir))
|
---|
37 | if output:
|
---|
38 | return Popen([cmd], shell=True, stdout=PIPE, cwd=dir).communicate()[0]
|
---|
39 | elif checkfail:
|
---|
40 | return check_call(cmd, shell=True, cwd=dir)
|
---|
41 | else:
|
---|
42 | return call(cmd, shell=True, cwd=dir)
|
---|
43 |
|
---|
44 | def find_git_root():
|
---|
45 | '''get to the top of the git repo'''
|
---|
46 | p=os.getcwd()
|
---|
47 | while p != '/':
|
---|
48 | if os.path.isdir(os.path.join(p, ".git")):
|
---|
49 | return p
|
---|
50 | p = os.path.abspath(os.path.join(p, '..'))
|
---|
51 | return None
|
---|
52 |
|
---|
53 | cwd = os.getcwd()
|
---|
54 | gitroot = find_git_root()
|
---|
55 |
|
---|
56 | # create a bisect script
|
---|
57 | f = tempfile.NamedTemporaryFile(delete=False)
|
---|
58 | f.write("set -x\n")
|
---|
59 | f.write("cd %s || exit 125\n" % cwd)
|
---|
60 | if opts.autogen:
|
---|
61 | f.write("%s || exit 125\n" % opts.autogen_command)
|
---|
62 | if opts.configure:
|
---|
63 | f.write("%s || exit 125\n" % opts.configure_command)
|
---|
64 | if opts.clean:
|
---|
65 | f.write("make clean || exit 125\n")
|
---|
66 | if opts.skip_build_errors:
|
---|
67 | build_err = 125
|
---|
68 | else:
|
---|
69 | build_err = 1
|
---|
70 | f.write("%s || exit %u\n" % (opts.build_command, build_err))
|
---|
71 | f.write("%s || exit 1\n" % opts.test_command)
|
---|
72 | f.write("exit 0\n")
|
---|
73 | f.close()
|
---|
74 |
|
---|
75 | def cleanup():
|
---|
76 | run_cmd("git bisect reset", dir=gitroot)
|
---|
77 | os.unlink(f.name)
|
---|
78 | sys.exit(-1)
|
---|
79 |
|
---|
80 | # run bisect
|
---|
81 | ret = -1
|
---|
82 | try:
|
---|
83 | run_cmd("git bisect reset", dir=gitroot, show=False, checkfail=False)
|
---|
84 | run_cmd("git bisect start %s %s --" % (opts.bad, opts.good), dir=gitroot)
|
---|
85 | ret = run_cmd("git bisect run bash %s" % f.name, dir=gitroot, show=True, checkfail=False)
|
---|
86 | except KeyboardInterrupt:
|
---|
87 | print("Cleaning up")
|
---|
88 | cleanup()
|
---|
89 | except Exception, reason:
|
---|
90 | print("Failed bisect: %s" % reason)
|
---|
91 | cleanup()
|
---|
92 |
|
---|
93 | run_cmd("git bisect reset", dir=gitroot)
|
---|
94 | os.unlink(f.name)
|
---|
95 | sys.exit(ret)
|
---|