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