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 :
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,7 @@ def getCompilerHash(compilerBinary):
900943
901944
902945def getFileHashes (filePaths ):
903- if 'CLCACHE_SERVER' in os . environ :
946+ if GlobalSettings . getValue ( 'CLCACHE_SERVER' ) not in [ None , '0' , 'false' , 'False' ] :
904947 pipeName = r'\\.\pipe\clcache_srv'
905948 while True :
906949 try :
@@ -939,7 +982,7 @@ def getStringHash(dataString):
939982
940983
941984def expandBasedirPlaceholder (path ):
942- baseDir = normalizeBaseDir (os . environ . get ('CLCACHE_BASEDIR' ))
985+ baseDir = normalizeBaseDir (GlobalSettings . getValue ('CLCACHE_BASEDIR' ))
943986 if path .startswith (BASEDIR_REPLACEMENT ):
944987 if not baseDir :
945988 raise LogicException ('No CLCACHE_BASEDIR set, but found relative path ' + path )
@@ -949,7 +992,7 @@ def expandBasedirPlaceholder(path):
949992
950993
951994def collapseBasedirToPlaceholder (path ):
952- baseDir = normalizeBaseDir (os . environ . get ('CLCACHE_BASEDIR' ))
995+ baseDir = normalizeBaseDir (GlobalSettings . getValue ('CLCACHE_BASEDIR' ))
953996 if baseDir is None :
954997 return path
955998 else :
@@ -972,7 +1015,7 @@ def ensureDirectoryExists(path):
9721015def copyOrLink (srcFilePath , dstFilePath ):
9731016 ensureDirectoryExists (os .path .dirname (os .path .abspath (dstFilePath )))
9741017
975- if "CLCACHE_HARDLINK" in os . environ :
1018+ if GlobalSettings . getValue ( "CLCACHE_HARDLINK" ) not in [ None , '0' , 'false' , 'False' ] :
9761019 ret = windll .kernel32 .CreateHardLinkW (str (dstFilePath ), str (srcFilePath ), None )
9771020 if ret != 0 :
9781021 # Touch the time stamp of the new link so that the build system
@@ -998,11 +1041,10 @@ def myExecutablePath():
9981041
9991042
10001043def findCompilerBinary ():
1001- if "CLCACHE_CL" in os . environ :
1002- path = os . environ [ "CLCACHE_CL" ]
1044+ path = GlobalSettings . getValue ( "CLCACHE_CL" )
1045+ if path :
10031046 if os .path .basename (path ) == path :
10041047 path = which (path )
1005-
10061048 return path if os .path .exists (path ) else None
10071049
10081050 frozenByPy2Exe = hasattr (sys , "frozen" )
@@ -1020,7 +1062,7 @@ def findCompilerBinary():
10201062
10211063
10221064def printTraceStatement (msg : str ) -> None :
1023- if "CLCACHE_LOG" in os . environ :
1065+ if GlobalSettings . getValue ( "CLCACHE_LOG" ) not in [ None , '0' , 'false' , 'False' ] :
10241066 scriptDir = os .path .realpath (os .path .dirname (sys .argv [0 ]))
10251067 with OUTPUT_LOCK :
10261068 print (os .path .join (scriptDir , "clcache.py" ) + " " + msg )
@@ -1571,7 +1613,7 @@ def main():
15711613 printTraceStatement ("Found real compiler binary at '{0!s}'" .format (compiler ))
15721614 printTraceStatement ("Arguments we care about: '{}'" .format (sys .argv ))
15731615
1574- if "CLCACHE_DISABLE" in os . environ :
1616+ if GlobalSettings . getValue ( "CLCACHE_DISABLE" ) not in [ None , '0' , 'false' , 'False' ] :
15751617 return invokeRealCompiler (compiler , sys .argv [1 :])[0 ]
15761618 try :
15771619 return processCompileRequest (cache , compiler , sys .argv )
@@ -1671,7 +1713,7 @@ def processSingleSource(compiler, cmdLine, sourceFile, objectFile, environment):
16711713 assert objectFile is not None
16721714 cache = Cache ()
16731715
1674- if 'CLCACHE_NODIRECT' in os . environ :
1716+ if GlobalSettings . getValue ( 'CLCACHE_NODIRECT' ) not in [ None , '0' , 'false' , 'False' ] :
16751717 return processNoDirect (cache , objectFile , compiler , cmdLine , environment )
16761718 else :
16771719 return processDirect (cache , objectFile , compiler , cmdLine , sourceFile )
@@ -1770,7 +1812,7 @@ def ensureArtifactsExist(cache, cachekey, reason, objectFile, compilerResult, ex
17701812
17711813
17721814if __name__ == '__main__' :
1773- if 'CLCACHE_PROFILE' in os . environ :
1815+ if GlobalSettings . getValue ( 'CLCACHE_PROFILE' ) not in [ None , '0' , 'false' , 'False' ] :
17741816 INVOCATION_HASH = getStringHash (',' .join (sys .argv ))
17751817 cProfile .run ('main()' , filename = 'clcache-{}.prof' .format (INVOCATION_HASH ))
17761818 else :
0 commit comments