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