blob: ee51ed3d140779dc5e386884d2a6e3306ba12213 [file] [log] [blame]
#!/usr/bin/env python
# *************************************************************
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
#
# *************************************************************
# ------------------------------------------------------------------------------
# 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, [])