Skip to content

Commit

Permalink
Add support of precompiled headers
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
izmmisha committed Sep 3, 2018
1 parent abe10f0 commit 2b54b8d
Showing 1 changed file with 53 additions and 3 deletions.
56 changes: 53 additions & 3 deletions clcache/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,14 @@ def allSectionsLocked(repository):
section.lock.release()


def readPchHash(pchFile):
with open(pchFile+".clcache", 'r') as f:
return f.read()

def writePchHash(pchFile, hashSum):
with open(pchFile+".clcache", 'w') as f:
f.write('{}'.format(hashSum))

class ManifestRepository:
# Bump this counter whenever the current manifest file format changes.
# E.g. changing the file format from {'oldkey': ...} to {'newkey': ...} requires
Expand Down Expand Up @@ -333,6 +341,10 @@ def getManifestHash(compilerBinary, commandLine, sourceFile):

additionalData = "{}|{}|{}".format(
compilerHash, commandLine, ManifestRepository.MANIFEST_FILE_FORMAT_VERSION)

if 'Yu' in arguments:
pchFile = CommandLineAnalyzer.getPchFileName(arguments, sourceFile)
additionalData += readPchHash(pchFile)
return getFileHash(sourceFile, additionalData)

@staticmethod
Expand Down Expand Up @@ -519,6 +531,10 @@ def computeKeyNodirect(compilerBinary, commandLine, environment):
h.update(preprocessedSourceCode)
return h.hexdigest()

@staticmethod
def computePchKey(cacheKey):
return cacheKey + "-pch"

@staticmethod
def _normalizedCommandLine(cmdline):
# Remove all arguments from the command line which only influence the
Expand Down Expand Up @@ -1308,6 +1324,20 @@ def parseArgumentsAndInputFiles(cmdline):

return dict(arguments), inputFiles

@staticmethod
def getPchFileName(options, inputFile):
if 'Fp' in options:
return options['Fp'][0]
if 'Yc' in options:
headerName = options['Yc'][0]
elif 'Yu' in options:
headerName = options['Yu'][0]
else:
return None
if not headerName:
headerName = inputFile
return basenameWithoutExtension(headerName) + '.pch'

@staticmethod
def analyze(cmdline: List[str]) -> Tuple[List[Tuple[str, str]], List[str]]:
options, inputFiles = CommandLineAnalyzer.parseArgumentsAndInputFiles(cmdline)
Expand Down Expand Up @@ -1336,9 +1366,6 @@ def analyze(cmdline: List[str]) -> Tuple[List[Tuple[str, str]], List[str]]:
if 'Zi' in options:
raise ExternalDebugInfoError()

if 'Yc' in options or 'Yu' in options:
raise CalledWithPchError()

if 'link' in options or 'c' not in options:
raise CalledForLinkError()

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

if 'Yc' in options:
objectFiles = [(objectFile, CommandLineAnalyzer.getPchFileName(options, inputFile[0]))
for inputFile, objectFile in zip(inputFiles, objectFiles)]

printTraceStatement("Compiler source files: {}".format(inputFiles))
printTraceStatement("Compiler object file: {}".format(objectFiles))
return inputFiles, objectFiles
Expand Down Expand Up @@ -1531,6 +1562,7 @@ def addObjectToCache(stats, cache, cachekey, artifacts):

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

with cache.lockFor(cachekey):
with cache.statistics.lock, cache.statistics as stats:
Expand All @@ -1541,6 +1573,11 @@ def processCacheHit(cache, objectFile, cachekey):

cachedArtifacts = cache.getEntry(cachekey)
copyOrLink(cachedArtifacts.objectFilePath, objectFile)
if pchFile is not None:
pchCachedArtifacts = cache.getEntry(CompilerArtifactsRepository.computePchKey(cachekey))
copyOrLink(pchCachedArtifacts.objectFilePath, pchFile)
writePchHash(pchFile, cachekey)

printTraceStatement("Finished. Exit code 0")
return 0, cachedArtifacts.stdout, cachedArtifacts.stderr, False

Expand Down Expand Up @@ -1804,6 +1841,8 @@ def processNoDirect(cache, objectFile, compiler, cmdLine, environment):
def ensureArtifactsExist(cache, cachekey, reason, objectFile, compilerResult, extraCallable=None):
cleanupRequired = False
returnCode, compilerOutput, compilerStderr = compilerResult
objectFile, pchFile = objectFile if isinstance(objectFile, tuple) else (objectFile, None)

correctCompiliation = (returnCode == 0 and os.path.exists(objectFile))
with cache.lockFor(cachekey):
if not cache.hasEntry(cachekey):
Expand All @@ -1814,6 +1853,17 @@ def ensureArtifactsExist(cache, cachekey, reason, objectFile, compilerResult, ex
cleanupRequired = addObjectToCache(stats, cache, cachekey, artifacts)
if extraCallable and correctCompiliation:
extraCallable()

if pchFile:
writePchHash(pchFile, cachekey)
cachekey = CompilerArtifactsRepository.computePchKey(cachekey)
with cache.lockFor(cachekey):
if not cache.hasEntry(cachekey):
with cache.statistics.lock, cache.statistics as stats:
if correctCompiliation:
artifacts = CompilerArtifacts(pchFile, "", "")
cleanupRequired = addObjectToCache(stats, cache, cachekey, artifacts) or cleanupRequired

return returnCode, compilerOutput, compilerStderr, cleanupRequired


Expand Down

0 comments on commit 2b54b8d

Please sign in to comment.