1#!/usr/bin/python
2# -*- coding: utf-8 -*-
3
4import os
5from subprocess import call, check_call
6from random import randrange
7import getpass
8
9
10outfilename = "/tmp/cache-bisect." + getpass.getuser() + ".log"
11outfile = open(outfilename, 'w')
12print 'BBBISECT_BEGIN'
13#print >> outfile, 'BBBISECT_BEGIN'
14#print >> outfile, 'BBBISECT_BEGIN'
15#print >> outfile, 'BBBISECT_BEGIN'
16#print >> outfile, 'BBBISECT_BEGIN'
17#print >> outfile, 'BBBISECT_BEGIN'
18#print 'BBBISECT_BEGIN'
19
20source_dir = '/mnt/big/xp/src/lyx-1.6.x-bisect'  # must NOT end in a slash
21cache_dir = source_dir + '.cache/'  # must end in a slash
22source_dir = '/mnt/big/xp/src/lyx-1.6.x-bisect2'  # must NOT end in a slash
23
24#make_cmd = 'autoconf && ./configure && cd src && echo __________ `pwd` && sleep 9 && make'
25make_cmd = 'rm -rf autom4te.cache && autoconf && ./configure && cd src && make'
26
27reverse_search = True
28reverse_search = False
29must_make = True  # If we fail to make the file, we could this a "bad" rather than "canot test"
30must_make = False
31
32# ToDo:
33# replace .tmp with .partial_copy and .not_yet_made
34
35
36def set_revision(new_v, tmp_d):
37    #check_call(['svn', 'up', '-r' + new_v, '--force'], cwd=tmp_d)
38    os.system ('cd "'+tmp_d+'" && yes tf | svn up -r'+new_v+'--force')
39
40def cmp_version(x, y):
41    return cmp(int(x), int(y))
42
43
44def get_cached_versions():
45    vers = [f for f in os.listdir(cache_dir) if not f.count('.')]
46    vers.sort(cmp_version)
47    return vers
48
49
50def version_in_range(v, lo, hi):
51    if cmp_version(v, lo) < 0:
52        return False
53    elif cmp_version(v, hi) > 0:
54        return False
55    return True
56
57def killall_p (s):
58    # Unlike killall, this searchs within command parameters, as well as the
59    # command name
60
61        #os.system("kPID=`ps a | grep '"+s+"' | grep -v grep | sed 's/^ *//g'|  sed 's/ .*$//'`
62    os.system("(kPID=`ps a | grep '"+s+
63    "' | grep -v grep | sed 's/^ *//g'|  sed 's/ .*$//'`\n\
64        echo kPID $kPID "+s+"\n\
65        echo kill $kPID\n\
66        kill $kPID\n\
67        sleep 0.1\n\
68        echo kill -9 $kPID\n\
69        kill -9 $kPID) 2> /dev/null")
70
71def clean_up ():
72    killall_p("autolyx")
73    killall_p("lyx")
74    killall_p("keytest.py")
75    killall_p("xclip")
76
77def filter_versions(vers, lo, hi):
78    return [v for v in vers if cmp]
79
80
81def ver2dir(v):
82    return cache_dir + v
83
84
85def make_ver(new_v, old_v=None, alt_v=None):
86    print 'MAKING', new_v, old_v, alt_v
87    new_d = ver2dir(new_v)
88    if old_v is None:
89        old_d = source_dir
90    else:
91        old_d = ver2dir(old_v)
92    fail_d = new_d + '.fail'
93    tmp_d = new_d + '.tmp'
94    if os.path.exists(cache_dir + fail_d):
95        return 1
96    if os.path.exists(new_d):
97        return 0
98    if not os.path.exists(tmp_d):
99        if not os.path.exists(old_d):
100            old_d = old_d + '.tmp'
101        call(['rm', '-rf', tmp_d + '.cp'])
102        call(['cp', '-rvu', old_d, tmp_d + '.cp'])
103        check_call(['mv', tmp_d + '.cp', tmp_d])
104        set_revision(new_v, tmp_d)
105    call('pwd && sleep 5 && echo ' + make_cmd, cwd=tmp_d, shell=True)
106    result = call(make_cmd, cwd=tmp_d, shell=True)
107    if result == 0:
108        print 'Make successful'
109        check_call(['mv', tmp_d, new_d])
110    return result
111
112
113def change_after(cmd, v):
114    result = run_cmd(cmd, v)
115    ca = result_after(result)
116    print >> outfile, 'BISECT_change_after', v, ca
117    print 'BISECT_change_after', v, ca
118    return ca
119
120
121def change_before(cmd, v):
122    result = run_cmd(cmd, v)
123    cb = result_before(result)
124    print >> outfile, 'BISECT_change_before', v, cb
125    print 'BISECT_change_before', v, cb
126    return cb
127
128
129def result_after(i):
130    if reverse_search:
131        return result_bad(i)
132    else:
133        return result_good(i)
134
135
136def result_before(i):
137    if reverse_search:
138        return result_good(i)
139    else:
140        return result_bad(i)
141
142
143def result_good(i):
144    return i == 0
145
146
147def result_bad(i):
148    return not result_ugly(i) and not result_good(i)
149
150
151def result_ugly(i):
152    return i == 125  # Like git, we treat 125 as "We cannot test this version"
153
154
155def run_cmd(cmd, v):
156    #result = call('pwd ; echo SS ' + cmd, shell=True, cwd=ver2dir(v))
157    print "CMD", cmd
158    print "V2D", ver2dir(v)
159    os
160    #result = subprocess.call(cmd, shell=True, cwd=ver2dir(v))
161    result = call(cmd, cwd=ver2dir(v))
162    clean_up()
163    print cmd, result
164    return result
165
166
167def do_bisect(cmd, vers, build):
168    lo = 0
169    hi = len(vers) - 1
170    m = (lo + hi) / 2
171
172    print lo, hi, m
173    print vers[lo], vers[hi], vers[m]
174    print vers
175
176    print >> outfile, 'VERS', final_vers
177
178    while len(vers) > 2:
179        print 'i', lo, hi, m, cmd
180        print 'v', vers[lo], vers[hi], vers[m], cmd
181        print vers
182
183        print '#ugly = Nonese'
184
185        if build or must_make:
186            ugly = False
187            result = make_ver(vers[m], vers[lo], vers[hi])
188            print 'AMKE RESULT', result
189            if not must_make:
190                if result > 0 and not must_make:
191                    ugly = True  # Not good, or bad, just ugly.
192                else:
193                    result = run_cmd(cmd, vers[m])
194        if not ugly:
195            if result > 127:
196                os._exit(1)
197            ugly = result_ugly(result)
198        if ugly:
199            print vers[m] + ' is UGLY'
200            del vers[m]
201            hi = len(vers) - 1
202            m = randrange(0, len(vers))
203        else:
204            if result_after(result):
205                print vers[m] + ' is AFTER'
206                del vers[lo:m]
207            else:
208                print vers[m] + ' is BEFORE'
209                del vers[m + 1:hi + 1]
210            hi = len(vers) - 1
211            m = (lo + hi) / 2
212
213        print 'VERS REMAINING:', vers
214
215    return vers
216
217
218def check_bisect(cmd, vers):
219    lo = 0
220    hi = len(vers) - 1
221    l = vers[lo]
222    h = vers[hi]
223    if make_ver(l):
224        return False
225    if make_ver(h):
226        return False
227    if change_before(cmd, l):
228        print 'Cannot bisect, change before ' + l\
229             + ' or regression test invalid'
230        return False
231    if change_after(cmd, h):
232        print 'Cannot bisect, change after ' + h\
233             + ' or regression test invalid'
234        return False
235    return True
236
237
238def do_check_bisect(cmd, vers, build):
239    print vers
240    if check_bisect(cmd, vers):
241        return do_bisect(cmd, vers, build)
242    else:
243        return
244
245
246def open_and_readlines(fname):
247    f = open(fname, 'r')
248    lines = f.readlines()
249    for i in range(0, len(lines)):
250        lines[i] = lines[i].rstrip('\n')
251    return lines
252
253
254def get_versions_between(l, h):
255    vers = [f for f in open_and_readlines('all_versions')
256            if version_in_range(f, l, h)]
257    vers.sort(cmp_version)
258    return vers
259
260
261def get_cached_versions_between(l, h):
262    vers = [f for f in get_cached_versions() if version_in_range(f, l, h)]
263    vers.sort(cmp_version)
264    print 'BTWN', l, h, vers
265    return vers
266
267
268def two_level_bisect(cmd, LO, HI):
269    if make_ver(LO):
270        return False
271    if make_ver(HI):
272        return False
273    vers = get_cached_versions_between(LO, HI)
274    print 'CACHED_VERSIONS', vers
275    vers = do_check_bisect(cmd, vers, False)
276    print 'Closest Cached Versions', vers
277    if vers is None:
278        return
279    if len(vers) != 2:
280        return
281    vers = get_versions_between(vers[0], vers[1])
282    print 'BETWEEN VERSIONS', vers
283    vers = do_check_bisect(cmd, vers, True)
284
285
286def multisect(cmd, vers):
287    i = 1
288    while i < len(vers):
289        print >> outfile, 'MULTISECT', vers[i]
290        print 'MULTISECT', vers[i]
291        if not ( make_ver(vers[i], vers[i-1])==0 and change_after(cmd, vers[i]) ) :
292            i = i + 1
293        else:
294            return two_level_bisect(cmd, vers[i], vers[0])
295
296
297print 'BISECT_BEGIN'
298print >> outfile, 'BISECT_BEGIN'
299outfile.flush()
300#final_vers = multisect('$TEST_COMMAND', ['30614', '27418', '23000'])
301cmd = os.sys.argv
302del cmd[0]
303
304VERS = os.environ.get('MULTISECT_VERS')
305if VERS is None:
306    VERS = ['30614', '27418', '23000']
307else:
308    VERS = VERS.split()
309
310final_vers = multisect(cmd, VERS)
311#final_vers = two_level_bisect('true', "21107","23000")
312#final_vers = do_bisect('true', get_versions_between("21107","23000"),True)
313outfile.flush()
314print
315print >> outfile, 'BISECT_FINAL', final_vers
316print 'BISECT_FINAL', final_vers
317os.system('echo BISECT_BEGIN >> /tmp/adsfadsf.log')
318os.system('echo BISECT_FINAL >> /tmp/adsfadsf.log')
319
320clean_up()
321os._exit(0)
322