import argparse
import os
import subprocess
import sys
def is_path(s):
return '/' in s
def run_test(test):
return subprocess.run([test], capture_output=True).returncode == 0
def modify_rsp(rsp_entries, other_rel_path, modify_after_num):
ret = []
for r in rsp_entries:
if is_path(r):
if modify_after_num == 0:
r = os.path.join(other_rel_path, r)
else:
modify_after_num -= 1
ret.append(r)
assert modify_after_num == 0
return ret
def test_modified_rsp(test, modified_rsp_entries, rsp_path):
with open(rsp_path, 'w') as f:
f.write(' '.join(modified_rsp_entries))
return run_test(test)
def bisect(test, zero_result, rsp_entries, num_files_in_rsp, other_rel_path, rsp_path):
lower = 0
upper = num_files_in_rsp
while lower != upper - 1:
assert lower < upper - 1
mid = int((lower + upper) / 2)
assert lower != mid and mid != upper
print('Trying {} ({}-{})'.format(mid, lower, upper))
result = test_modified_rsp(test, modify_rsp(rsp_entries, other_rel_path, mid),
rsp_path)
if zero_result == result:
lower = mid
else:
upper = mid
return upper
def main():
parser = argparse.ArgumentParser()
parser.add_argument('--test',
help='Binary to test if current setup is good or bad',
required=True)
parser.add_argument('--rsp', help='rsp file', required=True)
parser.add_argument(
'--other-rel-path',
help='Relative path from current build directory to other build ' +
'directory, e.g. from "out/Default" to "out/Other" specify "../Other"',
required=True)
args = parser.parse_args()
with open(args.rsp, 'r') as f:
rsp_entries = f.read()
rsp_entries = rsp_entries.split()
num_files_in_rsp = sum(1 for a in rsp_entries if is_path(a))
if num_files_in_rsp == 0:
print('No files in rsp?')
return 1
print('{} files in rsp'.format(num_files_in_rsp))
try:
print('Initial testing')
test0 = test_modified_rsp(args.test, modify_rsp(rsp_entries, args.other_rel_path,
0), args.rsp)
test_all = test_modified_rsp(
args.test, modify_rsp(rsp_entries, args.other_rel_path, num_files_in_rsp),
args.rsp)
if test0 == test_all:
print('Test returned same exit code for both build directories')
return 1
print('First build directory returned ' + ('0' if test_all else '1'))
result = bisect(args.test, test0, rsp_entries, num_files_in_rsp,
args.other_rel_path, args.rsp)
print('First file change: {} ({})'.format(
list(filter(is_path, rsp_entries))[result - 1], result))
rsp_out_0 = args.rsp + '.0'
rsp_out_1 = args.rsp + '.1'
with open(rsp_out_0, 'w') as f:
f.write(' '.join(modify_rsp(rsp_entries, args.other_rel_path, result - 1)))
with open(rsp_out_1, 'w') as f:
f.write(' '.join(modify_rsp(rsp_entries, args.other_rel_path, result)))
print('Bisection point rsp files written to {} and {}'.format(
rsp_out_0, rsp_out_1))
finally:
with open(args.rsp, 'w') as f:
f.write(' '.join(rsp_entries))
if __name__ == '__main__':
sys.exit(main())