Package pygccxml :: Package parser :: Module directory_cache

Source Code for Module pygccxml.parser.directory_cache

  1  # Copyright 2004-2008 Roman Yakovenko.
 
  2  # Distributed under the Boost Software License, Version 1.0. (See
 
  3  # accompanying file LICENSE_1_0.txt or copy at
 
  4  # http://www.boost.org/LICENSE_1_0.txt)
 
  5  #
 
  6  # The initial version of the directory_cache_t class was written
 
  7  # by Matthias Baas (baas@ira.uka.de).
 
  8  
 
  9  """Directory cache implementation.
 
 10  
 
 11  This module contains the implementation of a cache that uses individual
 
 12  files stored in a dedicated cache directory to store the cached contents.
 
 13  The cache class is L{directory_cache_t} which can be passed to the C{cache}
 
 14  argument of the L{parse()} function.
 
 15  """ 
 16  
 
 17  import os, os.path, gzip, md5 
 18  import cPickle 
 19  import declarations_cache 
 20  
 
21 -class index_entry_t:
22 """Entry of the index table in the directory cache index. 23 24 Each cached header file (i.e. each *.cache file) has a corresponding 25 index_entry_t object. This object is used to determine whether the 26 cache file with the declarations is still valid or not. 27 28 This class is a helper class for the directory_cache_t class. 29 """ 30
31 - def __init__( self, filesigs, configsig ):
32 """Constructor. 33 34 filesigs is a list of tuples (fileid, sig)... 35 configsig is the signature of the configuration object. 36 """ 37 self.filesigs = filesigs 38 self.configsig = configsig
39
40 - def __getstate__(self):
41 return (self.filesigs, self.configsig)
42
43 - def __setstate__(self, state):
44 self.filesigs, self.configsig = state
45 46
47 -class directory_cache_t ( declarations_cache.cache_base_t ):
48 """Cache class that stores its data as multiple files inside a directory. 49 50 The cache stores one index file called "index.dat" which is always 51 read by the cache when the cache object is created. Each header file 52 will have its corresponding *.cache file that stores the declarations 53 found in the header file. The index file is used to determine whether 54 a *.cache file is still valid or not (by checking if one of the dependent 55 files (i.e. the header file itself and all included files) have been 56 modified since the last run). 57 """ 58
59 - def __init__( self, dir="cache", compression=False, md5_sigs=True ):
60 """Constructor. 61 62 dir is the cache directory (it is created if it does not exist). 63 If compression is set to True the cache files will be compressed 64 using gzip. 65 md5_sigs determines whether file modifications is checked by 66 computing a md5 digest or by checking the modification date. 67 """ 68 declarations_cache.cache_base_t.__init__(self) 69 70 # Cache directory 71 self.__dir = os.path.abspath(dir) 72 73 # Flag that determines whether the cache files will be compressed 74 self.__compression = compression 75 76 # Flag that determines whether the signature is a md5 digest or 77 # the modification time 78 # (this flag is passed to the filename_repository_t class) 79 self.__md5_sigs = md5_sigs 80 81 # Filename repository 82 self.__filename_rep = filename_repository_t(self.__md5_sigs) 83 84 # Index dictionary (Key is the value returned by _create_cache_key() 85 # (which is based on the header file name) and value is an 86 # index_entry_t object) 87 self.__index = {} 88 89 # Flag that indicates whether the index was modified 90 self.__modified_flag = False 91 92 # Check if dir refers to an existing file... 93 if os.path.isfile(self.__dir): 94 raise ValueError, "Cannot use %s as cache directory. There is already a file with that name."%self.__dir 95 96 # Load the cache or create the cache directory... 97 if os.path.isdir(self.__dir): 98 self._load() 99 else: 100 # Create the cache directory... 101 os.mkdir(self.__dir)
102
103 - def flush(self):
104 """Save the index table to disk.""" 105 106 self._save()
107 # self.__filename_rep._dump() 108
109 - def update(self, source_file, configuration, declarations, included_files):
110 """Replace a cache entry by a new value. 111 112 @param source_file: Header file name. 113 @type source_file: str 114 @param configuration: Configuration object. 115 @type configuration: L{config_t} 116 @param declarations: Declarations contained in the header file. 117 @type declarations: picklable object 118 @param included_files: Dependent files 119 @type included_files: list of str 120 """ 121 # Normlize all paths... 122 source_file = os.path.normpath(source_file) 123 included_files = map(lambda p: os.path.normpath(p), included_files) 124 125 # Create the list of dependent files. This is the included_files list 126 # + the source file. Duplicate names are removed. 127 dependent_files = {} 128 for name in [source_file]+included_files: 129 dependent_files[name] = 1 130 dependent_files = dependent_files.keys() 131 132 key = self._create_cache_key(source_file) 133 # Remove an existing entry (if there is one) 134 # After calling this method, it is guaranteed that __index[key] 135 # does not exist anymore. 136 self._remove_entry(source_file, key) 137 138 # Create a new entry... 139 140 # Create the sigs of all dependent files... 141 filesigs = [] 142 for filename in dependent_files: 143 id_,sig = self.__filename_rep.acquire_filename(filename) 144 filesigs.append((id_,sig)) 145 146 configsig = self._create_config_signature(configuration) 147 entry = index_entry_t(filesigs, configsig) 148 self.__index[key] = entry 149 self.__modified_flag = True 150 151 # Write the declarations into the cache file... 152 cachefilename = self._create_cache_filename(source_file) 153 self._write_file(cachefilename, declarations)
154 155
156 - def cached_value(self, source_file, configuration):
157 """Return the cached declarations or None. 158 159 @param source_file: Header file name 160 @type source_file: str 161 @param configuration: Configuration object 162 @type configuration: L{config_t} 163 @return: Cached declarations or None 164 """ 165 166 # Check if the cache contains an entry for source_file 167 key = self._create_cache_key(source_file) 168 entry = self.__index.get(key) 169 if entry==None: 170 # print "CACHE: %s: Not cached"%source_file 171 return None 172 173 # Check if the entry is still valid. It is not valid if: 174 # - the source_file has been updated 175 # - the configuration object has changed (i.e. the header is parsed 176 # by gccxml with different settings which may influence the 177 # declarations) 178 # - the included files have been updated 179 # (this list is part of the cache entry as it cannot be known 180 # by the caller when cached_value() is called. It was instead 181 # passed to update()) 182 183 # Check if the config is different... 184 configsig = self._create_config_signature(configuration) 185 if configsig!=entry.configsig: 186 # print "CACHE: %s: Config mismatch"%source_file 187 return None 188 189 # Check if any of the dependent files has been modified... 190 for id_, sig in entry.filesigs: 191 if self.__filename_rep.is_file_modified(id_, sig): 192 # print "CACHE: %s: Entry not up to date"%source_file 193 return None 194 195 # Load and return the cached declarations 196 cachefilename = self._create_cache_filename(source_file) 197 decls = self._read_file(cachefilename) 198 199 # print "CACHE: Using cached decls for",source_file 200 return decls
201
202 - def _load(self):
203 """Load the cache. 204 205 Loads the file index.dat which contains the index table and 206 the file name repository. 207 208 This method is called by the constructor. 209 """ 210 211 indexfilename = os.path.join(self.__dir, "index.dat") 212 if os.path.exists(indexfilename): 213 data = self._read_file(indexfilename) 214 self.__index = data[0] 215 self.__filename_rep = data[1] 216 if self.__filename_rep._md5_sigs!=self.__md5_sigs: 217 print "CACHE: Warning: md5_sigs stored in the cache is set to %s."%self.__filename_rep._md5_sigs 218 print " Please remove the cache to change this setting." 219 self.__md5_sigs = self.__filename_rep._md5_sigs 220 else: 221 self.__index = {} 222 self.__filename_rep = filename_repository_t(self.__md5_sigs) 223 224 self.__modified_flag = False
225
226 - def _save(self):
227 """Save the cache index if it was modified. 228 229 Saves the index table and the file name repository in the file 230 index.dat. 231 """ 232 if self.__modified_flag: 233 self.__filename_rep.update_id_counter() 234 indexfilename = os.path.join(self.__dir, "index.dat") 235 self._write_file