@@ -91,6 +91,14 @@ def normalizeBaseDir(baseDir):
9191 return None
9292
9393
94+ class IncludeNotFoundException (Exception ):
95+ pass
96+
97+
98+ class IncludeChangedException (Exception ):
99+ pass
100+
101+
94102class CacheLockException (Exception ):
95103 pass
96104
@@ -118,7 +126,7 @@ def setManifest(self, manifestHash, manifest):
118126 ensureDirectoryExists (self .manifestSectionDir )
119127 with open (self .manifestPath (manifestHash ), 'w' ) as outFile :
120128 # Converting namedtuple to JSON via OrderedDict preserves key names and keys order
121- json .dump (manifest ._asdict (), outFile , indent = 2 )
129+ json .dump (manifest ._asdict (), outFile , sort_keys = True , indent = 2 )
122130
123131 def getManifest (self , manifestHash ):
124132 fileName = self .manifestPath (manifestHash )
@@ -138,7 +146,7 @@ class ManifestRepository(object):
138146 # invalidation, such that a manifest that was stored using the old format is not
139147 # interpreted using the new format. Instead the old file will not be touched
140148 # again due to a new manifest hash and is cleaned away after some time.
141- MANIFEST_FILE_FORMAT_VERSION = 3
149+ MANIFEST_FILE_FORMAT_VERSION = 4
142150
143151 def __init__ (self , manifestsRootDir ):
144152 self ._manifestsRootDir = manifestsRootDir
@@ -184,8 +192,22 @@ def getManifestHash(compilerBinary, commandLine, sourceFile):
184192 return getFileHash (sourceFile , additionalData )
185193
186194 @staticmethod
187- def getIncludesContentHashForFiles (listOfIncludesAbsolute ):
188- listOfIncludesHashes = [getFileHash (filepath ) for filepath in listOfIncludesAbsolute ]
195+ def getIncludesContentHashForFiles (includes ):
196+ listOfIncludesHashes = []
197+ includeMissing = False
198+
199+ for path in sorted (includes .keys ()):
200+ try :
201+ fileHash = getFileHash (path )
202+ if fileHash != includes [path ]:
203+ raise IncludeChangedException ()
204+ listOfIncludesHashes .append (fileHash )
205+ except FileNotFoundError :
206+ includeMissing = True
207+
208+ if includeMissing :
209+ raise IncludeNotFoundException ()
210+
189211 return ManifestRepository .getIncludesContentHashForHashes (listOfIncludesHashes )
190212
191213 @staticmethod
@@ -1224,12 +1246,14 @@ def clearCache(cache):
12241246 print ('Cache cleared' )
12251247
12261248
1227- # Returns pair - list of includes and new compiler output.
1249+ # Returns pair:
1250+ # 1. set of include filepaths
1251+ # 2. new compiler output
12281252# Output changes if strip is True in that case all lines with include
12291253# directives are stripped from it
1230- def parseIncludesList (compilerOutput , sourceFile , strip ):
1254+ def parseIncludesSet (compilerOutput , sourceFile , strip ):
12311255 newOutput = []
1232- includesSet = set ([] )
1256+ includesSet = set ()
12331257
12341258 # Example lines
12351259 # Note: including file: C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\INCLUDE\limits.h
@@ -1256,9 +1280,9 @@ def parseIncludesList(compilerOutput, sourceFile, strip):
12561280 elif strip :
12571281 newOutput .append (line )
12581282 if strip :
1259- return sorted ( includesSet ) , '' .join (newOutput )
1283+ return includesSet , '' .join (newOutput )
12601284 else :
1261- return sorted ( includesSet ) , compilerOutput
1285+ return includesSet , compilerOutput
12621286
12631287
12641288def addObjectToCache (stats , cache , cachekey , artifacts ):
@@ -1294,17 +1318,34 @@ def postprocessObjectEvicted(cache, objectFile, cachekey, compilerResult):
12941318 return compilerResult
12951319
12961320
1297- def postprocessHeaderChangedMiss (
1298- cache , objectFile , manifestSection , manifest , manifestHash , includesContentHash , compilerResult ):
1321+ def createManifest (manifestHash , includePaths ):
1322+ baseDir = normalizeBaseDir (os .environ .get ('CLCACHE_BASEDIR' ))
1323+
1324+ includes = {path :getFileHash (path ) for path in includePaths }
1325+ includesContentHash = ManifestRepository .getIncludesContentHashForFiles (includes )
12991326 cachekey = Cache .getDirectCacheKey (manifestHash , includesContentHash )
1327+
1328+ # Create new manifest
1329+ if baseDir :
1330+ relocatableIncludePaths = {
1331+ collapseBasedirToPlaceholder (path , baseDir ):contentHash
1332+ for path , contentHash in includes .items ()
1333+ }
1334+ manifest = Manifest (relocatableIncludePaths , {})
1335+ else :
1336+ manifest = Manifest (includes , {})
1337+ manifest .includesContentToObjectMap [includesContentHash ] = cachekey
1338+ return manifest , cachekey
1339+
1340+
1341+ def postprocessHeaderChangedMiss (
1342+ cache , objectFile , manifestSection , manifestHash , sourceFile , compilerResult , stripIncludes ):
13001343 returnCode , compilerOutput , compilerStderr = compilerResult
1344+ includePaths , compilerOutput = parseIncludesSet (compilerOutput , sourceFile , stripIncludes )
13011345
13021346 removedItems = []
13031347 if returnCode == 0 and os .path .exists (objectFile ):
1304- while len (manifest .includesContentToObjectMap ) >= MAX_MANIFEST_HASHES :
1305- _ , objectHash = manifest .includesContentToObjectMap .popitem ()
1306- removedItems .append (objectHash )
1307- manifest .includesContentToObjectMap [includesContentHash ] = cachekey
1348+ manifest , cachekey = createManifest (manifestHash , includePaths )
13081349
13091350 with cache .lock , cache .statistics as stats :
13101351 stats .registerHeaderChangedMiss ()
@@ -1313,27 +1354,19 @@ def postprocessHeaderChangedMiss(
13131354 cache .compilerArtifactsRepository .removeObjects (stats , removedItems )
13141355 manifestSection .setManifest (manifestHash , manifest )
13151356
1316- return compilerResult
1357+ return returnCode , compilerOutput , compilerStderr
13171358
13181359
13191360def postprocessNoManifestMiss (
1320- cache , objectFile , manifestSection , manifestHash , baseDir , sourceFile , compilerResult , stripIncludes ):
1361+ cache , objectFile , manifestSection , manifestHash , sourceFile , compilerResult , stripIncludes ):
13211362 returnCode , compilerOutput , compilerStderr = compilerResult
1322- listOfIncludes , compilerOutput = parseIncludesList (compilerOutput , sourceFile , stripIncludes )
1363+ includePaths , compilerOutput = parseIncludesSet (compilerOutput , sourceFile , stripIncludes )
13231364
13241365 manifest = None
13251366 cachekey = None
13261367
13271368 if returnCode == 0 and os .path .exists (objectFile ):
1328- # Store compile output and manifest
1329- if baseDir :
1330- relocatableIncludePaths = [collapseBasedirToPlaceholder (path , baseDir ) for path in listOfIncludes ]
1331- manifest = Manifest (relocatableIncludePaths , {})
1332- else :
1333- manifest = Manifest (listOfIncludes , {})
1334- includesContentHash = ManifestRepository .getIncludesContentHashForFiles (listOfIncludes )
1335- cachekey = Cache .getDirectCacheKey (manifestHash , includesContentHash )
1336- manifest .includesContentToObjectMap [includesContentHash ] = cachekey
1369+ manifest , cachekey = createManifest (manifestHash , includePaths )
13371370
13381371 with cache .lock , cache .statistics as stats :
13391372 stats .registerSourceChangedMiss ()
@@ -1469,32 +1502,44 @@ def processDirect(cache, objectFile, compiler, cmdLine, sourceFile):
14691502 manifestHash = ManifestRepository .getManifestHash (compiler , cmdLine , sourceFile )
14701503 manifestSection = cache .manifestRepository .section (manifestHash )
14711504 with cache .lock :
1505+ createNewManifest = False
14721506 manifest = manifestSection .getManifest (manifestHash )
14731507 if manifest is not None :
14741508 # NOTE: command line options already included in hash for manifest name
1475- includesContentHash = ManifestRepository .getIncludesContentHashForFiles (
1476- [expandBasedirPlaceholder (include , baseDir ) for include in manifest .includeFiles ])
1477- cachekey = manifest .includesContentToObjectMap .get (includesContentHash )
1478- if cachekey is not None :
1509+ try :
1510+ includesContentHash = ManifestRepository .getIncludesContentHashForFiles ({
1511+ expandBasedirPlaceholder (path , baseDir ):contentHash
1512+ for path , contentHash in manifest .includeFiles .items ()
1513+ })
1514+
1515+ cachekey = manifest .includesContentToObjectMap .get (includesContentHash )
1516+ assert cachekey is not None
14791517 if cache .compilerArtifactsRepository .section (cachekey ).hasEntry (cachekey ):
14801518 return processCacheHit (cache , objectFile , cachekey )
14811519 else :
14821520 postProcessing = lambda compilerResult : postprocessObjectEvicted (
14831521 cache , objectFile , cachekey , compilerResult )
1484- else :
1522+ except IncludeChangedException :
1523+ createNewManifest = True
14851524 postProcessing = lambda compilerResult : postprocessHeaderChangedMiss (
1486- cache , objectFile , manifestSection , manifest , manifestHash , includesContentHash , compilerResult )
1525+ cache , objectFile , manifestSection , manifestHash , sourceFile , compilerResult , stripIncludes )
1526+ except IncludeNotFoundException :
1527+ # register nothing. This is probably just a compile error
1528+ postProcessing = None
14871529 else :
1488- origCmdLine = cmdLine
1489- stripIncludes = False
1490- if '/showIncludes' not in cmdLine :
1491- cmdLine = ['/showIncludes' ] + origCmdLine
1492- stripIncludes = True
1530+ createNewManifest = True
14931531 postProcessing = lambda compilerResult : postprocessNoManifestMiss (
1494- cache , objectFile , manifestSection , manifestHash , baseDir , sourceFile , compilerResult , stripIncludes )
1532+ cache , objectFile , manifestSection , manifestHash , sourceFile , compilerResult , stripIncludes )
1533+
1534+ if createNewManifest :
1535+ stripIncludes = False
1536+ if '/showIncludes' not in cmdLine :
1537+ cmdLine .insert (0 , '/showIncludes' )
1538+ stripIncludes = True
14951539
14961540 compilerResult = invokeRealCompiler (compiler , cmdLine , captureOutput = True )
1497- compilerResult = postProcessing (compilerResult )
1541+ if postProcessing :
1542+ compilerResult = postProcessing (compilerResult )
14981543 printTraceStatement ("Finished. Exit code {0:d}" .format (compilerResult [0 ]))
14991544 return compilerResult
15001545
0 commit comments