summaryrefslogtreecommitdiff
path: root/solenv/bin/pchdelta.py
blob: 52a0df4ecc5dccc967fbebb6f55ed72d1f609b35 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
#!/usr/bin/python

# ------------------------------------------------------------------------------
# Hacky little delta debug tool to figure out the proper includes for a pch file
#
# Usage:
# 
# pchdelta.py <pch_target> <dir1> [<dir2> <dir3> ...]
#
# <pch_target>      File to perform delta debugging on. The section to test
#                   is delimeted by '//---MARKER---' lines.
# <dir1> .. <dirn>  Sequence of directories to run dmake in to test if the
#                   modification works
#
# Examples:
#
# pchdelta.py inc/pch/precompiled_sfx2.hxx inc source/dialog
#
#  Run pchdelta inside sfx2 first building the pch files and then files in
# source/dialog
#
# ------------------------------------------------------------------------------

import os
import os.path
import sys

# C++
MARKER="//---MARKER---\n"

# dmake
#MARKER="#---MARKER---\n"

# ------------------------------------------------------------------------------
# Sequentially build all argument directories from scratch

def testSequenceBuild(dirlist):
    cwd = os.path.abspath(os.getcwd())
    for path in dirlist:
        os.chdir(path)
        buildcommand = "dmake -u"
        buildcommand += " >>" + cwd + "/buildlog.txt 2>&1"
        buildresult = os.system(buildcommand)
        os.chdir(cwd)
        if buildresult != 0:
            return False
    return True

# ------------------------------------------------------------------------------
# Dump out the delta file with corresponding markers

def writePch(pchname, header, footer, acceptedlines, testlines):
    outputfile = file(pchname, "w")
    outputfile.write(header)
    outputfile.write(MARKER)
    outputfile.write("\n".join(acceptedlines))
    if len(testlines) > 0:
        outputfile.write("\n\n//---Candidate marker---\n")
        outputfile.write("\n".join(testlines) + "\n")
        outputfile.write("//---Candidate marker end---\n")
    outputfile.write(MARKER)
    outputfile.write(footer)
    outputfile.close()
    

# ------------------------------------------------------------------------------
# Recursive tester routine. Test the segment given and if an error is
# encountered splits the segment into <fanout> subsegment and recurses. Failing
# one liners are rejected. The set of accepted lines are built sequentially from
# the beginning.

def binaryTest(dirlist, lines, pchname, header, footer, acceptedlines, indent, startpoint):
    linecount = len(lines)
    if linecount == 0:
        return
    # Test if this slice passes the buildtest
    writePch(pchname, header, footer, acceptedlines, lines)
    if testSequenceBuild(dirlist):
        return acceptedlines + lines

    # Reject one liners
    if linecount == 1:
        print indent + "Rejected: " + lines[0]
        return acceptedlines

    # Recurse with multiline slices
    fanout = 4
    splits = []
    for i in range(3):
        splits.append(linecount * (i + 1) / fanout)
    splits.append(linecount)

    splitstart = 0
    for splitend in splits:
        # avoid splitting in case we have no resulting lines
        if (splitend - splitstart) == 0:
            continue
        splitslice = lines[splitstart:splitend]
        print indent + "[" + str(startpoint + splitstart) + ":" + str(startpoint + splitend) + "] (" + str(splitend - splitstart) + ")"
        acceptedlines = binaryTest(dirlist, splitslice, pchname, header, footer, acceptedlines, indent + " ", startpoint + splitstart)
        splitstart = splitend

    return acceptedlines

# ------------------------------------------------------------------------------
# Main entry point

if len(sys.argv) < 3:
    print "Usage: " + sys.argv[0] + " <pch_target> <dir1> [<dir2> <dir3> ...]"
    sys.exit(1)

pchname = os.path.abspath(sys.argv[1])
dirlist = sys.argv[2:]

# remove old build log file
if os.path.exists("buildlog.txt"):
    os.remove("buildlog.txt")

# test for corner case of everything working from the start
if testSequenceBuild(dirlist):
    print "pch working, nothing to do."
    sys.exit(0)

# Open the header file for reading
inputfile = file(pchname, "r+")
inputdata = inputfile.read()
inputfile.close()

segments = inputdata.split(MARKER)
header = segments[0]
footer = segments[2]
lines = segments[1].split("\n")

writePch(pchname + "_backup", header, footer, lines, [])

# test for corner case of no convergence possible
writePch(pchname, header, footer, [], [])
if not testSequenceBuild(dirlist):
    writePch(pchname, header, footer, lines, [])
    print "Building with no candidate lines failed. Convergence questionable, aborting."
    sys.exit(0)

# Starting pruning
print "Starting evaluation of " + str(len(lines)) + " lines"
acceptedlines = binaryTest(dirlist, lines, pchname, header, footer, [], "", 0)
writePch(pchname, header, footer, acceptedlines, [])