mirror of
https://github.com/nillerusr/source-engine.git
synced 2024-12-22 14:16:50 +00:00
167 lines
6.6 KiB
Python
167 lines
6.6 KiB
Python
"""
|
|
This script is designed to fix dependency problems with PCH files and tlog files.
|
|
VS 2010 puts dependency information in cl.read.1.tlog files, and sometimes (for
|
|
unknown reasons that may be related to a PS3 Addin) the list of dependencies for
|
|
the source file that generates a PCH file are corrupt. In this situation there are
|
|
only four or five files listed. Those four (the fifth file is optional) files are
|
|
always:
|
|
|
|
C:\PROGRAM FILES (X86)\MICROSOFT VISUAL STUDIO 10.0\VC\BIN\1033\CLUI.DLL
|
|
C:\WINDOWS\GLOBALIZATION\SORTING\SORTDEFAULT.NLS
|
|
C:\WINDOWS\SYSTEM32\RSAENH.DLL
|
|
C:\WINDOWS\SYSTEM32\TZRES.DLL
|
|
C:\PROGRAM FILES (X86)\MICROSOFT VISUAL STUDIO 10.0\COMMON7\IDE\MSPDBSRV.EXE
|
|
|
|
The only known reliable fix appears to be to delete all files from the directory
|
|
containing the tlog file. This forces a full rebuild of that project, which may
|
|
be happening earlier than necessary, but it is better than having a build not
|
|
happen when it should.
|
|
|
|
For more (not necessarily accurate) information see this post:
|
|
http://social.msdn.microsoft.com/Forums/is/msbuild/thread/df873f5d-9d9f-4e8a-80ec-01042a9a7022
|
|
|
|
The problem is probably at least partially due to a bug in VC++ where it will
|
|
incrementally update a PCH file, without reading all of the header files, thus
|
|
messing up the MSBuild dependency tracker. This is Microsoft bug TFS # 412600.
|
|
"""
|
|
|
|
import os
|
|
import codecs
|
|
import sys
|
|
import glob
|
|
import re
|
|
|
|
|
|
# Determine by pattern matching whether a source file name is a PCH generator.
|
|
# These are heuristics, but as long as we do sane naming they should work fine.
|
|
def IsPCHSourceFile(filepath):
|
|
filepath = filepath.lower()
|
|
filepart = os.path.split(filepath)[1]
|
|
# Common PCH source file names
|
|
if filepart == "stdafx.cpp":
|
|
return True
|
|
if filepart == "cbase.cpp":
|
|
return True
|
|
|
|
# Common PCH source file patterns
|
|
if filepart.count("pch.cpp") > 0:
|
|
return True
|
|
if filepart.count("pch_") > 0:
|
|
return True
|
|
|
|
# Broken accidental PCH source file names
|
|
if filepart == "ivp_compact_ledge_gen.cxx":
|
|
return True
|
|
if filepart == "ivp_physic.cxx":
|
|
return True
|
|
|
|
|
|
# Check to see if dependencies for currentSource are plausible
|
|
# If not then delete all the files from the diretory that the .tlog file is
|
|
# in.
|
|
def CheckDependencies(currentSource, currentDependencies, tlogPath):
|
|
if IsPCHSourceFile(currentSource):
|
|
headersFound = False
|
|
for dependency in currentDependencies:
|
|
if dependency.lower()[-2:] == ".h":
|
|
headersFound = True
|
|
# Across dozens of samples this bug always happens with four or
|
|
# five dependencies. There are some legitimate cases where there is just a single
|
|
# dependency, which is why I don't check for <= 4.
|
|
if not headersFound and (len(currentDependencies) == 4 or len(currentDependencies) == 5):
|
|
buildDir = os.path.split(tlogPath)[0]
|
|
objectName = os.path.split(currentSource)[1]
|
|
objectName = os.path.splitext(objectName)[0] + ".obj"
|
|
objectPath = os.path.join(buildDir, objectName)
|
|
print ""
|
|
print "%s(1): warning : Bogus dependencies for %s." % (tlogPath, currentSource)
|
|
print "Only %d dependencies found in %s." % (len(currentDependencies), tlogPath)
|
|
print "Dependencies are:"
|
|
for dependency in currentDependencies:
|
|
print " %s" % dependency
|
|
|
|
# Nuking the object file associated with the currentSource file forces a rebuild,
|
|
# but it does not seem to reliably update the .tlog file.
|
|
# Similarly, nuking the tlog file(s) does not seem to reliably force a rebuild.
|
|
# Also, nuking all of the object files does not seem to reliably update the .tlog files
|
|
# These techniques are all supposed to work but due to an incremental PCH rebuild
|
|
# 'feature' in Visual Studio they do not. The VS compiler will detect that no header
|
|
# files have changed and will rebuild stdafx.cpp *using* the .pch file. This means
|
|
# that MSBuild determines that there are *no* header file dependencies, so the .tlog
|
|
# file gets permanently corrupted.
|
|
|
|
# This must (and does) force a rebuild that updates the .tlog file.
|
|
files = glob.glob(os.path.join(buildDir, "*"))
|
|
print "Removing all %d files from %s to force a rebuild." % (len(files), buildDir)
|
|
for anyFile in files:
|
|
os.remove(anyFile)
|
|
elif not headersFound:
|
|
print ""
|
|
print "No headers found in %d dependencies for %s" % (len(currentDependencies), tlogPath)
|
|
for dependency in currentDependencies:
|
|
print " %s" % dependency
|
|
|
|
|
|
# This is called for each cl.read.1.tlog file in the tree
|
|
def ParseTLogFile(tlogPath):
|
|
# Read all of the lines in the tlog file:
|
|
lines = codecs.open(tlogPath, encoding="utf-16").readlines()
|
|
|
|
# Iterate through all the lines. Lines that start with ^ are source files.
|
|
# Lines that follow are the dependencies for that file. That's it.
|
|
currentSource = ""
|
|
for line in lines:
|
|
# Strip off the carriage-return and line-feed character
|
|
line = line.strip()
|
|
#print line
|
|
if line[0] == "^":
|
|
if len(currentSource) > 0:
|
|
CheckDependencies(currentSource, currentDependencies, tlogPath)
|
|
currentSource = line[1:]
|
|
currentDependencies = []
|
|
else:
|
|
currentDependencies.append(line)
|
|
|
|
if len(currentSource) > 0:
|
|
CheckDependencies(currentSource, currentDependencies, tlogPath)
|
|
|
|
|
|
tlogDroppingParser = re.compile(r"cl\.\d+\..+\.1\.tlog")
|
|
|
|
deletedDroppingsCount = 0
|
|
|
|
# Two-byte files of the form cl.1234.*.1.tlog (where * is either 'read' or
|
|
# 'write') build up without limit in the intermediate directories. The number
|
|
# is the PID of the compiler. Some builders had over 300,000 of these files and
|
|
# they noticeably slow down directory scanning. As long as we're iterating we
|
|
# might as well delete them -- they can be deleted anytime a compile is not in progress.
|
|
def DeleteExcessTLogFiles(dirname, filesindir):
|
|
global deletedDroppingsCount
|
|
for fname in filesindir:
|
|
fname = fname.lower()
|
|
if tlogDroppingParser.match(fname):
|
|
tlogDroppingPath = os.path.join(dirname, fname)
|
|
os.remove(tlogDroppingPath)
|
|
deletedDroppingsCount += 1
|
|
|
|
|
|
# This is called for each directory in the tree.
|
|
def lister(dummy, dirname, filesindir):
|
|
for fname in filesindir:
|
|
if fname.lower() == "cl.read.1.tlog":
|
|
# Run this first because ParseTLogFile might delete them which
|
|
# would cause exceptions on this step.
|
|
# We only need to run this on directories that contain
|
|
# a cl.read.1.tlog file since these are the intermediate
|
|
# directories.
|
|
DeleteExcessTLogFiles(dirname, filesindir)
|
|
tlogPath = os.path.join(dirname, fname)
|
|
ParseTLogFile(tlogPath)
|
|
|
|
|
|
os.path.walk(".", lister, None)
|
|
|
|
# MSBuild accumulates these files without limit. Other files also accumulate but these
|
|
# ones account for 99% of the problem.
|
|
print "%d tlog droppings (cl.*.*.1.tlog temporary files) deleted." % deletedDroppingsCount
|