1
2
3
4
5
6
7
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
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
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
71 self.__dir = os.path.abspath(dir)
72
73
74 self.__compression = compression
75
76
77
78
79 self.__md5_sigs = md5_sigs
80
81
82 self.__filename_rep = filename_repository_t(self.__md5_sigs)
83
84
85
86
87 self.__index = {}
88
89
90 self.__modified_flag = False
91
92
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
97 if os.path.isdir(self.__dir):
98 self._load()
99 else:
100
101 os.mkdir(self.__dir)
102
104 """Save the index table to disk."""
105
106 self._save()
107
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
122 source_file = os.path.normpath(source_file)
123 included_files = map(lambda p: os.path.normpath(p), included_files)
124
125
126
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
134
135
136 self._remove_entry(source_file, key)
137
138
139
140
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
152 cachefilename = self._create_cache_filename(source_file)
153 self._write_file(cachefilename, declarations)
154
155
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
167 key = self._create_cache_key(source_file)
168 entry = self.__index.get(key)
169 if entry==None:
170
171 return None
172
173
174
175
176
177
178
179
180
181
182
183
184 configsig = self._create_config_signature(configuration)
185 if configsig!=entry.configsig:
186
187 return None
188
189
190 for id_, sig in entry.filesigs:
191 if self.__filename_rep.is_file_modified(id_, sig):
192
193 return None
194
195
196 cachefilename = self._create_cache_filename(source_file)
197 decls = self._read_file(cachefilename)
198
199
200 return decls
201
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
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