xref: /trunk/main/solenv/bin/pchdelta.py (revision cdf0e10c)
1#!/usr/bin/python
2
3# ------------------------------------------------------------------------------
4# Hacky little delta debug tool to figure out the proper includes for a pch file
5#
6# Usage:
7#
8# pchdelta.py <pch_target> <dir1> [<dir2> <dir3> ...]
9#
10# <pch_target>      File to perform delta debugging on. The section to test
11#                   is delimeted by '//---MARKER---' lines.
12# <dir1> .. <dirn>  Sequence of directories to run dmake in to test if the
13#                   modification works
14#
15# Examples:
16#
17# pchdelta.py inc/pch/precompiled_sfx2.hxx inc source/dialog
18#
19#  Run pchdelta inside sfx2 first building the pch files and then files in
20# source/dialog
21#
22# ------------------------------------------------------------------------------
23
24import os
25import os.path
26import sys
27
28# C++
29MARKER="//---MARKER---\n"
30
31# dmake
32#MARKER="#---MARKER---\n"
33
34# ------------------------------------------------------------------------------
35# Sequentially build all argument directories from scratch
36
37def testSequenceBuild(dirlist):
38    cwd = os.path.abspath(os.getcwd())
39    for path in dirlist:
40        os.chdir(path)
41        buildcommand = "dmake -u"
42        buildcommand += " >>" + cwd + "/buildlog.txt 2>&1"
43        buildresult = os.system(buildcommand)
44        os.chdir(cwd)
45        if buildresult != 0:
46            return False
47    return True
48
49# ------------------------------------------------------------------------------
50# Dump out the delta file with corresponding markers
51
52def writePch(pchname, header, footer, acceptedlines, testlines):
53    outputfile = file(pchname, "w")
54    outputfile.write(header)
55    outputfile.write(MARKER)
56    outputfile.write("\n".join(acceptedlines))
57    if len(testlines) > 0:
58        outputfile.write("\n\n//---Candidate marker---\n")
59        outputfile.write("\n".join(testlines) + "\n")
60        outputfile.write("//---Candidate marker end---\n")
61    outputfile.write(MARKER)
62    outputfile.write(footer)
63    outputfile.close()
64
65
66# ------------------------------------------------------------------------------
67# Recursive tester routine. Test the segment given and if an error is
68# encountered splits the segment into <fanout> subsegment and recurses. Failing
69# one liners are rejected. The set of accepted lines are built sequentially from
70# the beginning.
71
72def binaryTest(dirlist, lines, pchname, header, footer, acceptedlines, indent, startpoint):
73    linecount = len(lines)
74    if linecount == 0:
75        return
76    # Test if this slice passes the buildtest
77    writePch(pchname, header, footer, acceptedlines, lines)
78    if testSequenceBuild(dirlist):
79        return acceptedlines + lines
80
81    # Reject one liners
82    if linecount == 1:
83        print indent + "Rejected: " + lines[0]
84        return acceptedlines
85
86    # Recurse with multiline slices
87    fanout = 4
88    splits = []
89    for i in range(3):
90        splits.append(linecount * (i + 1) / fanout)
91    splits.append(linecount)
92
93    splitstart = 0
94    for splitend in splits:
95        # avoid splitting in case we have no resulting lines
96        if (splitend - splitstart) == 0:
97            continue
98        splitslice = lines[splitstart:splitend]
99        print indent + "[" + str(startpoint + splitstart) + ":" + str(startpoint + splitend) + "] (" + str(splitend - splitstart) + ")"
100        acceptedlines = binaryTest(dirlist, splitslice, pchname, header, footer, acceptedlines, indent + " ", startpoint + splitstart)
101        splitstart = splitend
102
103    return acceptedlines
104
105# ------------------------------------------------------------------------------
106# Main entry point
107
108if len(sys.argv) < 3:
109    print "Usage: " + sys.argv[0] + " <pch_target> <dir1> [<dir2> <dir3> ...]"
110    sys.exit(1)
111
112pchname = os.path.abspath(sys.argv[1])
113dirlist = sys.argv[2:]
114
115# remove old build log file
116if os.path.exists("buildlog.txt"):
117    os.remove("buildlog.txt")
118
119# test for corner case of everything working from the start
120if testSequenceBuild(dirlist):
121    print "pch working, nothing to do."
122    sys.exit(0)
123
124# Open the header file for reading
125inputfile = file(pchname, "r+")
126inputdata = inputfile.read()
127inputfile.close()
128
129segments = inputdata.split(MARKER)
130header = segments[0]
131footer = segments[2]
132lines = segments[1].split("\n")
133
134writePch(pchname + "_backup", header, footer, lines, [])
135
136# test for corner case of no convergence possible
137writePch(pchname, header, footer, [], [])
138if not testSequenceBuild(dirlist):
139    writePch(pchname, header, footer, lines, [])
140    print "Building with no candidate lines failed. Convergence questionable, aborting."
141    sys.exit(0)
142
143# Starting pruning
144print "Starting evaluation of " + str(len(lines)) + " lines"
145acceptedlines = binaryTest(dirlist, lines, pchname, header, footer, [], "", 0)
146writePch(pchname, header, footer, acceptedlines, [])
147
148
149
150