You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
171 lines
5.4 KiB
171 lines
5.4 KiB
#!/usr/bin/env python |
|
|
|
''' |
|
A helper script for bisecting common problems when working with ArduPilot |
|
|
|
Bisect between a commit which builds and one which doesn't, |
|
finding the first commit which broke the build with a |
|
specific failure: |
|
|
|
git bisect good a7647e77d9 |
|
git bisect bad 153ad9539866f8d93a99e9998118bb090d2f747f |
|
cp -a Tools/autotest/bisect-helper.py /tmp |
|
git bisect run /tmp/bisect-helper.py --build \ |
|
--build-failure-string= \ |
|
"reference to 'OpticalFlow' is ambiguous" |
|
|
|
Work out who killed bebop: |
|
cp -a Tools/autotest/bisect-helper.py /tmp |
|
git bisect good a7647e77d9 && |
|
git bisect bad 153ad9539866f8d93a99e9998118bb090d2f747f && |
|
git bisect run /tmp/bisect-helper.py --build \ |
|
--waf-configure-arg="--board bebop" |
|
|
|
''' |
|
|
|
import optparse |
|
import os |
|
import subprocess |
|
import shlex |
|
import sys |
|
import time |
|
|
|
|
|
class Bisect(object): |
|
def __init__(self, opts): |
|
self.opts = opts |
|
|
|
def exit_skip(self): |
|
self.progress("SKIP") |
|
sys.exit(125) |
|
|
|
def exit_pass(self): |
|
self.progress("PASS") |
|
sys.exit(0) |
|
|
|
def exit_fail(self): |
|
self.progress("FAIL") |
|
sys.exit(1) |
|
|
|
def progress(self, string): |
|
'''pretty-print progress''' |
|
print("BH: %s" % string) |
|
|
|
def run_program(self, prefix, cmd_list): |
|
'''copied in from build_binaries.py''' |
|
'''run cmd_list, spewing and setting output in self''' |
|
self.progress("Running (%s)" % " ".join(cmd_list)) |
|
p = subprocess.Popen(cmd_list, |
|
bufsize=1, |
|
stdin=None, |
|
close_fds=True, |
|
stdout=subprocess.PIPE, |
|
stderr=subprocess.STDOUT) |
|
self.program_output = "" |
|
while True: |
|
x = p.stdout.readline() |
|
if len(x) == 0: |
|
returncode = os.waitpid(p.pid, 0) |
|
if returncode: |
|
break |
|
# select not available on Windows... probably... |
|
time.sleep(0.1) |
|
continue |
|
self.program_output += x |
|
x = x.rstrip() |
|
print("%s: %s" % (prefix, x)) |
|
(_, status) = returncode |
|
if status != 0: |
|
self.progress("Process failed (%s)" % |
|
str(returncode)) |
|
raise subprocess.CalledProcessError( |
|
returncode, cmd_list) |
|
|
|
def build(self): |
|
'''run ArduCopter build. May exit with skip or fail''' |
|
self.run_program("WAF-clean", ["./waf", "clean"]) |
|
cmd_configure = ["./waf", "configure"] |
|
pieces = [shlex.split(x) |
|
for x in opts.waf_configure_args] |
|
for piece in pieces: |
|
cmd_configure.extend(piece) |
|
self.run_program("WAF-configure", cmd_configure) |
|
cmd_build = ["./waf", "build"] |
|
pieces = [shlex.split(x) |
|
for x in opts.waf_build_args] |
|
for piece in pieces: |
|
cmd_build.extend(piece) |
|
try: |
|
self.run_program("WAF-build", cmd_build) |
|
except subprocess.CalledProcessError as e: |
|
# well, it definitely failed.... |
|
if opts.build_failure_string is not None: |
|
if opts.build_failure_string in self.program_output: |
|
self.progress("Found relevant build failure") |
|
self.exit_fail() |
|
# it failed, but not for the reason we're looking |
|
# for... |
|
self.exit_skip() |
|
else: |
|
self.exit_fail() |
|
|
|
|
|
class BisectBuild(Bisect): |
|
|
|
def __init__(self, opts): |
|
super(BisectBuild, self).__init__(opts) |
|
|
|
def run(self): |
|
self.build() # may exit with skip or fail |
|
self.exit_pass() |
|
|
|
|
|
class BisectCITest(Bisect): |
|
|
|
def __init__(self, opts): |
|
super(BisectCITest, self).__init__(opts) |
|
|
|
|
|
if __name__ == '__main__': |
|
|
|
parser = optparse.OptionParser("bisect.py ") |
|
parser.add_option("--build", |
|
action='store_true', |
|
default=False, |
|
help="Help bisect a build failure") |
|
parser.add_option("--build-failure-string", |
|
type='string', |
|
default=None, |
|
help="If supplied, must be present in" |
|
"build output to count as a failure") |
|
|
|
group_build = optparse.OptionGroup(parser, "Build options") |
|
group_build.add_option("", "--waf-configure-arg", |
|
action="append", |
|
dest="waf_configure_args", |
|
type="string", |
|
default=["--board skyviper-v2450"], |
|
help="extra arguments to pass to" |
|
"waf in configure step") |
|
group_build.add_option("", "--waf-build-arg", |
|
action="append", |
|
dest="waf_build_args", |
|
type="string", |
|
default=["--target bin/arducopter"], |
|
help="extra arguments to pass" |
|
"to waf in its build step") |
|
|
|
parser.add_option_group(group_build) |
|
|
|
(opts, args) = parser.parse_args() |
|
|
|
if opts.build: |
|
bisecter = BisectBuild(opts) |
|
else: |
|
bisecter = BisectCITest(opts) |
|
|
|
try: |
|
bisecter.run() |
|
except Exception as e: |
|
print("Caught exception in bisect-helper: %s" % str(e)) |
|
sys.exit(129) # should abort the bisect process
|
|
|