1#!/usr/bin/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 169 170 171