2424import sys
2525import threading
2626from tempfile import TemporaryFile
27- from typing import Any , List , Tuple , Iterator
27+ from typing import Any , Dict , List , Tuple , Iterator
2828
2929VERSION = "4.1.0-dev"
3030
@@ -308,6 +308,50 @@ def getIncludesContentHashForHashes(listOfHashes):
308308 return HashAlgorithm (',' .join (listOfHashes ).encode ()).hexdigest ()
309309
310310
311+ class GlobalSettings :
312+ """ Implements a common place to obain settings from. """
313+ @staticmethod
314+ def getValue (settingName , defaultValue = None ):
315+ value = os .environ .get (settingName , None )
316+ if value is None : # compare to None to allow empty values
317+ value = GlobalSettings ._getFromCache (settingName )
318+ return value if value is not None else defaultValue
319+
320+ # serves as a cache to only read the config file once
321+ _cache = {} # type: Dict[str, str]
322+
323+ @staticmethod
324+ def _getFromCache (settingName ):
325+ if not GlobalSettings ._cache :
326+ GlobalSettings ._readFromFile ()
327+ return GlobalSettings ._cache .get (settingName , None )
328+
329+ @staticmethod
330+ def _readFromFile ():
331+ GlobalSettings ._cache ['dummy' ] = 'dummy' # so that _readFromFile is only called once
332+
333+ # prefer config in current directory
334+ filename = os .path .join (os .getcwd (), "clcache.conf" )
335+
336+ # ..or in home directory..
337+ if not os .path .exists (filename ):
338+ filename = os .path .join (os .path .expanduser ("~" ), ".clcache" , "clcache.conf" )
339+
340+ # or in "sysconfdir" (%ALLUSERSPROFILE%)
341+ if not os .path .exists (filename ):
342+ dirname = os .environ .get ('ALLUSERSPROFILE' , None )
343+ filename = os .path .join (dirname if dirname else "C:\\ Users" , ".clcache" , "clcache.conf" )
344+ try :
345+ with open (filename ) as f :
346+ for line in f .readlines ():
347+ kv = line .split ("=" )
348+ if len (kv ) != 2 or kv [0 ].startswith ("#" ):
349+ continue
350+ GlobalSettings ._cache [kv [0 ].strip ()] = kv [1 ].split ("#" )[0 ].strip ()
351+ except IOError :
352+ pass # only ignore file access errors (including not-existing path)
353+
354+
311355class CacheLock :
312356 """ Implements a lock for the object cache which
313357 can be used in 'with' statements. """
@@ -359,7 +403,7 @@ def release(self):
359403
360404 @staticmethod
361405 def forPath (path ):
362- timeoutMs = int (os . environ . get ('CLCACHE_OBJECT_CACHE_TIMEOUT_MS' , 10 * 1000 ))
406+ timeoutMs = int (GlobalSettings . getValue ('CLCACHE_OBJECT_CACHE_TIMEOUT_MS' , 10 * 1000 ))
363407 lockName = path .replace (':' , '-' ).replace ('\\ ' , '-' )
364408 return CacheLock (lockName , timeoutMs )
365409
@@ -505,10 +549,8 @@ class CacheFileStrategy:
505549 def __init__ (self , cacheDirectory = None ):
506550 self .dir = cacheDirectory
507551 if not self .dir :
508- try :
509- self .dir = os .environ ["CLCACHE_DIR" ]
510- except KeyError :
511- self .dir = os .path .join (os .path .expanduser ("~" ), "clcache" )
552+ self .dir = GlobalSettings .getValue ("CLCACHE_DIR" ,
553+ os .path .join (os .path .expanduser ("~" ), "clcache" ))
512554
513555 manifestsRootDir = os .path .join (self .dir , "manifests" )
514556 ensureDirectoryExists (manifestsRootDir )
@@ -593,9 +635,10 @@ def clean(self, stats, maximumSize):
593635
594636class Cache :
595637 def __init__ (self , cacheDirectory = None ):
596- if os .environ .get ("CLCACHE_MEMCACHED" ):
638+ memcached = GlobalSettings .getValue ("CLCACHE_MEMCACHED" )
639+ if memcached and memcached not in ['0' , 'false' , 'False' ]:
597640 from .storage import CacheFileWithMemcacheFallbackStrategy
598- self .strategy = CacheFileWithMemcacheFallbackStrategy (os . environ . get ( "CLCACHE_MEMCACHED" ) ,
641+ self .strategy = CacheFileWithMemcacheFallbackStrategy (memcached ,
599642 cacheDirectory = cacheDirectory )
600643 else :
601644 self .strategy = CacheFileStrategy (cacheDirectory = cacheDirectory )
@@ -900,7 +943,8 @@ def getCompilerHash(compilerBinary):
900943
901944
902945def getFileHashes (filePaths ):
903- if 'CLCACHE_SERVER' in os .environ :
946+ server = GlobalSettings .getValue ('CLCACHE_SERVER' )
947+ if server and server not in ['0' , 'false' , 'False' ]:
904948 pipeName = r'\\.\pipe\clcache_srv'
905949 while True :
906950 try :
@@ -939,7 +983,7 @@ def getStringHash(dataString):
939983
940984
941985def expandBasedirPlaceholder (path ):
942- baseDir = normalizeBaseDir (os . environ . get ('CLCACHE_BASEDIR' ))
986+ baseDir = normalizeBaseDir (GlobalSettings . getValue ('CLCACHE_BASEDIR' ))
943987 if path .startswith (BASEDIR_REPLACEMENT ):
944988 if not baseDir :
945989 raise LogicException ('No CLCACHE_BASEDIR set, but found relative path ' + path )
@@ -949,7 +993,7 @@ def expandBasedirPlaceholder(path):
949993
950994
951995def collapseBasedirToPlaceholder (path ):
952- baseDir = normalizeBaseDir (os . environ . get ('CLCACHE_BASEDIR' ))
996+ baseDir = normalizeBaseDir (GlobalSettings . getValue ('CLCACHE_BASEDIR' ))
953997 if baseDir is None :
954998 return path
955999 else :
@@ -971,8 +1015,8 @@ def ensureDirectoryExists(path):
9711015
9721016def copyOrLink (srcFilePath , dstFilePath ):
9731017 ensureDirectoryExists (os .path .dirname (os .path .abspath (dstFilePath )))
974-
975- if "CLCACHE_HARDLINK" in os . environ :
1018+ hardlink = GlobalSettings . getValue ( "CLCACHE_HARDLINK" )
1019+ if hardlink and hardlink not in [ '0' , 'false' , 'False' ] :
9761020 ret = windll .kernel32 .CreateHardLinkW (str (dstFilePath ), str (srcFilePath ), None )
9771021 if ret != 0 :
9781022 # Touch the time stamp of the new link so that the build system
@@ -998,11 +1042,10 @@ def myExecutablePath():
9981042
9991043
10001044def findCompilerBinary ():
1001- if "CLCACHE_CL" in os . environ :
1002- path = os . environ [ "CLCACHE_CL" ]
1045+ path = GlobalSettings . getValue ( "CLCACHE_CL" )
1046+ if path :
10031047 if os .path .basename (path ) == path :
10041048 path = which (path )
1005-
10061049 return path if os .path .exists (path ) else None
10071050
10081051 frozenByPy2Exe = hasattr (sys , "frozen" )
@@ -1020,7 +1063,8 @@ def findCompilerBinary():
10201063
10211064
10221065def printTraceStatement (msg : str ) -> None :
1023- if "CLCACHE_LOG" in os .environ :
1066+ clcachelog = GlobalSettings .getValue ("CLCACHE_LOG" )
1067+ if clcachelog and clcachelog not in ['0' , 'false' , 'False' ]:
10241068 scriptDir = os .path .realpath (os .path .dirname (sys .argv [0 ]))
10251069 with OUTPUT_LOCK :
10261070 print (os .path .join (scriptDir , "clcache.py" ) + " " + msg )
@@ -1570,8 +1614,8 @@ def main():
15701614
15711615 printTraceStatement ("Found real compiler binary at '{0!s}'" .format (compiler ))
15721616 printTraceStatement ("Arguments we care about: '{}'" .format (sys .argv ))
1573-
1574- if "CLCACHE_DISABLE" in os . environ :
1617+ enabled = GlobalSettings . getValue ( "CLCACHE_DISABLE" )
1618+ if enabled and enabled not in [ '0' , 'false' , 'False' ] :
15751619 return invokeRealCompiler (compiler , sys .argv [1 :])[0 ]
15761620 try :
15771621 return processCompileRequest (cache , compiler , sys .argv )
@@ -1670,8 +1714,8 @@ def processSingleSource(compiler, cmdLine, sourceFile, objectFile, environment):
16701714 try :
16711715 assert objectFile is not None
16721716 cache = Cache ()
1673-
1674- if 'CLCACHE_NODIRECT' in os . environ :
1717+ nodirect = GlobalSettings . getValue ( 'CLCACHE_NODIRECT' )
1718+ if nodirect and nodirect not in [ '0' , 'false' , 'False' ] :
16751719 return processNoDirect (cache , objectFile , compiler , cmdLine , environment )
16761720 else :
16771721 return processDirect (cache , objectFile , compiler , cmdLine , sourceFile )
@@ -1770,7 +1814,8 @@ def ensureArtifactsExist(cache, cachekey, reason, objectFile, compilerResult, ex
17701814
17711815
17721816if __name__ == '__main__' :
1773- if 'CLCACHE_PROFILE' in os .environ :
1817+ profilingEnabled = GlobalSettings .getValue ('CLCACHE_PROFILE' )
1818+ if profilingEnabled and profilingEnabled not in ['0' , 'false' , 'False' ]:
17741819 INVOCATION_HASH = getStringHash (',' .join (sys .argv ))
17751820 cProfile .run ('main()' , filename = 'clcache-{}.prof' .format (INVOCATION_HASH ))
17761821 else :
0 commit comments