Skip to content

Commit e09b445

Browse files
committed
Add support of precompiled headers
Creating PCH file (/Yc option) works in usual way as any other object, except that it will produce meta info (stdafx.pch.clcache file) contains it's cache key as identification of headers files and it's contents. Using PCH file (/Yu option) affects /showIncludes - it not contains PCH includes, so for generating manifest hash additionally used PCH cache key. So when PCH headers or it's content changed but no changes in source file or it's headers then it will produce different cache entry.
1 parent abe10f0 commit e09b445

File tree

1 file changed

+50
-3
lines changed

1 file changed

+50
-3
lines changed

clcache/__main__.py

Lines changed: 50 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,14 @@ def allSectionsLocked(repository):
271271
section.lock.release()
272272

273273

274+
def readPchHash(pchFile):
275+
with open(pchFile+".clcache", 'r') as f:
276+
return f.read()
277+
278+
def writePchHash(pchFile, hashSum):
279+
with open(pchFile+".clcache", 'w') as f:
280+
f.write('{}'.format(hashSum))
281+
274282
class ManifestRepository:
275283
# Bump this counter whenever the current manifest file format changes.
276284
# E.g. changing the file format from {'oldkey': ...} to {'newkey': ...} requires
@@ -333,6 +341,10 @@ def getManifestHash(compilerBinary, commandLine, sourceFile):
333341

334342
additionalData = "{}|{}|{}".format(
335343
compilerHash, commandLine, ManifestRepository.MANIFEST_FILE_FORMAT_VERSION)
344+
345+
if 'Yu' in arguments:
346+
pchFile = CommandLineAnalyzer.getPchFileName(arguments, sourceFile)
347+
additionalData += readPchHash(pchFile)
336348
return getFileHash(sourceFile, additionalData)
337349

338350
@staticmethod
@@ -1308,6 +1320,20 @@ def parseArgumentsAndInputFiles(cmdline):
13081320

13091321
return dict(arguments), inputFiles
13101322

1323+
@staticmethod
1324+
def getPchFileName(options, inputFile):
1325+
if 'Fp' in options:
1326+
return options['Fp'][0]
1327+
if 'Yc' in options:
1328+
pchName = options['Yc'][0]
1329+
elif 'Yu' in options:
1330+
pchName = options['Yu'][0]
1331+
else:
1332+
return None
1333+
if not pchName:
1334+
pchName = inputFile
1335+
return basenameWithoutExtension(pchName) + '.pch'
1336+
13111337
@staticmethod
13121338
def analyze(cmdline: List[str]) -> Tuple[List[Tuple[str, str]], List[str]]:
13131339
options, inputFiles = CommandLineAnalyzer.parseArgumentsAndInputFiles(cmdline)
@@ -1336,9 +1362,6 @@ def analyze(cmdline: List[str]) -> Tuple[List[Tuple[str, str]], List[str]]:
13361362
if 'Zi' in options:
13371363
raise ExternalDebugInfoError()
13381364

1339-
if 'Yc' in options or 'Yu' in options:
1340-
raise CalledWithPchError()
1341-
13421365
if 'link' in options or 'c' not in options:
13431366
raise CalledForLinkError()
13441367

@@ -1358,6 +1381,11 @@ def analyze(cmdline: List[str]) -> Tuple[List[Tuple[str, str]], List[str]]:
13581381
# Generate from .c/.cpp filenames
13591382
objectFiles = [os.path.join(prefix, basenameWithoutExtension(f)) + '.obj' for f, _ in inputFiles]
13601383

1384+
if 'Yc' in options:
1385+
assert len(objectFiles) == 1
1386+
pchFile = CommandLineAnalyzer.getPchFileName(options, inputFiles[0][0])
1387+
objectFiles = [(objectFiles[0], pchFile)]
1388+
13611389
printTraceStatement("Compiler source files: {}".format(inputFiles))
13621390
printTraceStatement("Compiler object file: {}".format(objectFiles))
13631391
return inputFiles, objectFiles
@@ -1531,6 +1559,7 @@ def addObjectToCache(stats, cache, cachekey, artifacts):
15311559

15321560
def processCacheHit(cache, objectFile, cachekey):
15331561
printTraceStatement("Reusing cached object for key {} for object file {}".format(cachekey, objectFile))
1562+
objectFile, pchFile = objectFile if isinstance(objectFile, tuple) else (objectFile, None)
15341563

15351564
with cache.lockFor(cachekey):
15361565
with cache.statistics.lock, cache.statistics as stats:
@@ -1541,6 +1570,11 @@ def processCacheHit(cache, objectFile, cachekey):
15411570

15421571
cachedArtifacts = cache.getEntry(cachekey)
15431572
copyOrLink(cachedArtifacts.objectFilePath, objectFile)
1573+
if pchFile is not None:
1574+
pchCachedArtifacts = cache.getEntry(cachekey+"-pch")
1575+
copyOrLink(pchCachedArtifacts.objectFilePath, pchFile)
1576+
writePchHash(pchFile, cachekey)
1577+
15441578
printTraceStatement("Finished. Exit code 0")
15451579
return 0, cachedArtifacts.stdout, cachedArtifacts.stderr, False
15461580

@@ -1804,6 +1838,8 @@ def processNoDirect(cache, objectFile, compiler, cmdLine, environment):
18041838
def ensureArtifactsExist(cache, cachekey, reason, objectFile, compilerResult, extraCallable=None):
18051839
cleanupRequired = False
18061840
returnCode, compilerOutput, compilerStderr = compilerResult
1841+
objectFile, pchFile = objectFile if isinstance(objectFile, tuple) else (objectFile, None)
1842+
18071843
correctCompiliation = (returnCode == 0 and os.path.exists(objectFile))
18081844
with cache.lockFor(cachekey):
18091845
if not cache.hasEntry(cachekey):
@@ -1814,6 +1850,17 @@ def ensureArtifactsExist(cache, cachekey, reason, objectFile, compilerResult, ex
18141850
cleanupRequired = addObjectToCache(stats, cache, cachekey, artifacts)
18151851
if extraCallable and correctCompiliation:
18161852
extraCallable()
1853+
1854+
if pchFile:
1855+
writePchHash(pchFile, cachekey)
1856+
cachekey = cachekey+"-pch"
1857+
with cache.lockFor(cachekey):
1858+
if not cache.hasEntry(cachekey):
1859+
with cache.statistics.lock, cache.statistics as stats:
1860+
if correctCompiliation:
1861+
artifacts = CompilerArtifacts(pchFile, "", "")
1862+
cleanupRequired = addObjectToCache(stats, cache, cachekey, artifacts) or cleanupRequired
1863+
18171864
return returnCode, compilerOutput, compilerStderr, cleanupRequired
18181865

18191866

0 commit comments

Comments
 (0)