#!/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 [ ...] # # File to perform delta debugging on. The section to test # is delimeted by '//---MARKER---' lines. # .. 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 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] + " [ ...]") 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, [])