Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

166 lines
6.6 KiB

  1. """
  2. This script is designed to fix dependency problems with PCH files and tlog files.
  3. VS 2010 puts dependency information in cl.read.1.tlog files, and sometimes (for
  4. unknown reasons that may be related to a PS3 Addin) the list of dependencies for
  5. the source file that generates a PCH file are corrupt. In this situation there are
  6. only four or five files listed. Those four (the fifth file is optional) files are
  7. always:
  8. C:\PROGRAM FILES (X86)\MICROSOFT VISUAL STUDIO 10.0\VC\BIN\1033\CLUI.DLL
  9. C:\WINDOWS\GLOBALIZATION\SORTING\SORTDEFAULT.NLS
  10. C:\WINDOWS\SYSTEM32\RSAENH.DLL
  11. C:\WINDOWS\SYSTEM32\TZRES.DLL
  12. C:\PROGRAM FILES (X86)\MICROSOFT VISUAL STUDIO 10.0\COMMON7\IDE\MSPDBSRV.EXE
  13. The only known reliable fix appears to be to delete all files from the directory
  14. containing the tlog file. This forces a full rebuild of that project, which may
  15. be happening earlier than necessary, but it is better than having a build not
  16. happen when it should.
  17. For more (not necessarily accurate) information see this post:
  18. http://social.msdn.microsoft.com/Forums/is/msbuild/thread/df873f5d-9d9f-4e8a-80ec-01042a9a7022
  19. The problem is probably at least partially due to a bug in VC++ where it will
  20. incrementally update a PCH file, without reading all of the header files, thus
  21. messing up the MSBuild dependency tracker. This is Microsoft bug TFS # 412600.
  22. """
  23. import os
  24. import codecs
  25. import sys
  26. import glob
  27. import re
  28. # Determine by pattern matching whether a source file name is a PCH generator.
  29. # These are heuristics, but as long as we do sane naming they should work fine.
  30. def IsPCHSourceFile(filepath):
  31. filepath = filepath.lower()
  32. filepart = os.path.split(filepath)[1]
  33. # Common PCH source file names
  34. if filepart == "stdafx.cpp":
  35. return True
  36. if filepart == "cbase.cpp":
  37. return True
  38. # Common PCH source file patterns
  39. if filepart.count("pch.cpp") > 0:
  40. return True
  41. if filepart.count("pch_") > 0:
  42. return True
  43. # Broken accidental PCH source file names
  44. if filepart == "ivp_compact_ledge_gen.cxx":
  45. return True
  46. if filepart == "ivp_physic.cxx":
  47. return True
  48. # Check to see if dependencies for currentSource are plausible
  49. # If not then delete all the files from the diretory that the .tlog file is
  50. # in.
  51. def CheckDependencies(currentSource, currentDependencies, tlogPath):
  52. if IsPCHSourceFile(currentSource):
  53. headersFound = False
  54. for dependency in currentDependencies:
  55. if dependency.lower()[-2:] == ".h":
  56. headersFound = True
  57. # Across dozens of samples this bug always happens with four or
  58. # five dependencies. There are some legitimate cases where there is just a single
  59. # dependency, which is why I don't check for <= 4.
  60. if not headersFound and (len(currentDependencies) == 4 or len(currentDependencies) == 5):
  61. buildDir = os.path.split(tlogPath)[0]
  62. objectName = os.path.split(currentSource)[1]
  63. objectName = os.path.splitext(objectName)[0] + ".obj"
  64. objectPath = os.path.join(buildDir, objectName)
  65. print ""
  66. print "%s(1): warning : Bogus dependencies for %s." % (tlogPath, currentSource)
  67. print "Only %d dependencies found in %s." % (len(currentDependencies), tlogPath)
  68. print "Dependencies are:"
  69. for dependency in currentDependencies:
  70. print " %s" % dependency
  71. # Nuking the object file associated with the currentSource file forces a rebuild,
  72. # but it does not seem to reliably update the .tlog file.
  73. # Similarly, nuking the tlog file(s) does not seem to reliably force a rebuild.
  74. # Also, nuking all of the object files does not seem to reliably update the .tlog files
  75. # These techniques are all supposed to work but due to an incremental PCH rebuild
  76. # 'feature' in Visual Studio they do not. The VS compiler will detect that no header
  77. # files have changed and will rebuild stdafx.cpp *using* the .pch file. This means
  78. # that MSBuild determines that there are *no* header file dependencies, so the .tlog
  79. # file gets permanently corrupted.
  80. # This must (and does) force a rebuild that updates the .tlog file.
  81. files = glob.glob(os.path.join(buildDir, "*"))
  82. print "Removing all %d files from %s to force a rebuild." % (len(files), buildDir)
  83. for anyFile in files:
  84. os.remove(anyFile)
  85. elif not headersFound:
  86. print ""
  87. print "No headers found in %d dependencies for %s" % (len(currentDependencies), tlogPath)
  88. for dependency in currentDependencies:
  89. print " %s" % dependency
  90. # This is called for each cl.read.1.tlog file in the tree
  91. def ParseTLogFile(tlogPath):
  92. # Read all of the lines in the tlog file:
  93. lines = codecs.open(tlogPath, encoding="utf-16").readlines()
  94. # Iterate through all the lines. Lines that start with ^ are source files.
  95. # Lines that follow are the dependencies for that file. That's it.
  96. currentSource = ""
  97. for line in lines:
  98. # Strip off the carriage-return and line-feed character
  99. line = line.strip()
  100. #print line
  101. if line[0] == "^":
  102. if len(currentSource) > 0:
  103. CheckDependencies(currentSource, currentDependencies, tlogPath)
  104. currentSource = line[1:]
  105. currentDependencies = []
  106. else:
  107. currentDependencies.append(line)
  108. if len(currentSource) > 0:
  109. CheckDependencies(currentSource, currentDependencies, tlogPath)
  110. tlogDroppingParser = re.compile(r"cl\.\d+\..+\.1\.tlog")
  111. deletedDroppingsCount = 0
  112. # Two-byte files of the form cl.1234.*.1.tlog (where * is either 'read' or
  113. # 'write') build up without limit in the intermediate directories. The number
  114. # is the PID of the compiler. Some builders had over 300,000 of these files and
  115. # they noticeably slow down directory scanning. As long as we're iterating we
  116. # might as well delete them -- they can be deleted anytime a compile is not in progress.
  117. def DeleteExcessTLogFiles(dirname, filesindir):
  118. global deletedDroppingsCount
  119. for fname in filesindir:
  120. fname = fname.lower()
  121. if tlogDroppingParser.match(fname):
  122. tlogDroppingPath = os.path.join(dirname, fname)
  123. os.remove(tlogDroppingPath)
  124. deletedDroppingsCount += 1
  125. # This is called for each directory in the tree.
  126. def lister(dummy, dirname, filesindir):
  127. for fname in filesindir:
  128. if fname.lower() == "cl.read.1.tlog":
  129. # Run this first because ParseTLogFile might delete them which
  130. # would cause exceptions on this step.
  131. # We only need to run this on directories that contain
  132. # a cl.read.1.tlog file since these are the intermediate
  133. # directories.
  134. DeleteExcessTLogFiles(dirname, filesindir)
  135. tlogPath = os.path.join(dirname, fname)
  136. ParseTLogFile(tlogPath)
  137. os.path.walk(".", lister, None)
  138. # MSBuild accumulates these files without limit. Other files also accumulate but these
  139. # ones account for 99% of the problem.
  140. print "%d tlog droppings (cl.*.*.1.tlog temporary files) deleted." % deletedDroppingsCount