1 """SCons.SConsign
2
3 Writing and reading information to the .sconsign file or files.
4
5 """
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30 __revision__ = "src/engine/SCons/SConsign.py 5023 2010/06/14 22:05:46 scons"
31
32 import SCons.compat
33
34 import os
35
36 import pickle
37
38 import SCons.dblite
39 import SCons.Warnings
40
44
45 SCons.dblite.ignore_corrupt_dbfiles = 1
46 SCons.dblite.corruption_warning = corrupt_dblite_warning
47
48
49 sig_files = []
50
51
52
53
54
55
56
57 DataBase = {}
58 DB_Module = SCons.dblite
59 DB_Name = ".sconsign"
60 DB_sync_list = []
61
63 global DataBase, DB_Module, DB_Name
64 top = dir.fs.Top
65 if not os.path.isabs(DB_Name) and top.repositories:
66 mode = "c"
67 for d in [top] + top.repositories:
68 if dir.is_under(d):
69 try:
70 return DataBase[d], mode
71 except KeyError:
72 path = d.entry_abspath(DB_Name)
73 try: db = DataBase[d] = DB_Module.open(path, mode)
74 except (IOError, OSError): pass
75 else:
76 if mode != "r":
77 DB_sync_list.append(db)
78 return db, mode
79 mode = "r"
80 try:
81 return DataBase[top], "c"
82 except KeyError:
83 db = DataBase[top] = DB_Module.open(DB_Name, "c")
84 DB_sync_list.append(db)
85 return db, "c"
86 except TypeError:
87 print "DataBase =", DataBase
88 raise
89
96
97 normcase = os.path.normcase
98
110
111 -class SConsignEntry(object):
112 """
113 Wrapper class for the generic entry in a .sconsign file.
114 The Node subclass populates it with attributes as it pleases.
115
116 XXX As coded below, we do expect a '.binfo' attribute to be added,
117 but we'll probably generalize this in the next refactorings.
118 """
119 current_version_id = 1
120 - def __init__(self):
121
122
123 _version_id = self.current_version_id
126 - def convert_from_sconsign(self, dir, name):
128
130 """
131 This is the controlling class for the signatures for the collection of
132 entries associated with a specific directory. The actual directory
133 association will be maintained by a subclass that is specific to
134 the underlying storage method. This class provides a common set of
135 methods for fetching and storing the individual bits of information
136 that make up signature entry.
137 """
139 self.entries = {}
140 self.dirty = False
141 self.to_be_merged = {}
142
143 - def get_entry(self, filename):
144 """
145 Fetch the specified entry attribute.
146 """
147 return self.entries[filename]
148
149 - def set_entry(self, filename, obj):
150 """
151 Set the entry.
152 """
153 self.entries[filename] = obj
154 self.dirty = True
155
156 - def do_not_set_entry(self, filename, obj):
158
164
167
169 for key, node in self.to_be_merged.items():
170 entry = node.get_stored_info()
171 try:
172 ninfo = entry.ninfo
173 except AttributeError:
174
175
176
177 pass
178 else:
179 ninfo.merge(node.get_ninfo())
180 self.entries[key] = entry
181 self.to_be_merged = {}
182
184 """
185 A Base subclass that reads and writes signature information
186 from a global .sconsign.db* file--the actual file suffix is
187 determined by the database module.
188 """
228
229 - def write(self, sync=1):
230 if not self.dirty:
231 return
232
233 self.merge()
234
235 db, mode = Get_DataBase(self.dir)
236
237
238
239
240
241 path = normcase(self.dir.path)
242 for key, entry in self.entries.items():
243 entry.convert_to_sconsign()
244 db[path] = pickle.dumps(self.entries, 1)
245
246 if sync:
247 try:
248 syncmethod = db.sync
249 except AttributeError:
250
251 pass
252 else:
253 syncmethod()
254
257 """
258 fp - file pointer to read entries from
259 """
260 Base.__init__(self)
261
262 if not fp:
263 return
264
265 self.entries = pickle.load(fp)
266 if not isinstance(self.entries, dict):
267 self.entries = {}
268 raise TypeError
269
270 if dir:
271 for key, entry in self.entries.items():
272 entry.convert_from_sconsign(dir, key)
273
275 """
276 Encapsulates reading and writing a per-directory .sconsign file.
277 """
301
302 - def write(self, sync=1):
303 """
304 Write the .sconsign file to disk.
305
306 Try to write to a temporary file first, and rename it if we
307 succeed. If we can't write to the temporary file, it's
308 probably because the directory isn't writable (and if so,
309 how did we build anything in this directory, anyway?), so
310 try to write directly to the .sconsign file as a backup.
311 If we can't rename, try to copy the temporary contents back
312 to the .sconsign file. Either way, always try to remove
313 the temporary file at the end.
314 """
315 if not self.dirty:
316 return
317
318 self.merge()
319
320 temp = os.path.join(self.dir.path, '.scons%d' % os.getpid())
321 try:
322 file = open(temp, 'wb')
323 fname = temp
324 except IOError:
325 try:
326 file = open(self.sconsign, 'wb')
327 fname = self.sconsign
328 except IOError:
329 return
330 for key, entry in self.entries.items():
331 entry.convert_to_sconsign()
332 pickle.dump(self.entries, file, 1)
333 file.close()
334 if fname != self.sconsign:
335 try:
336 mode = os.stat(self.sconsign)[0]
337 os.chmod(self.sconsign, 0666)
338 os.unlink(self.sconsign)
339 except (IOError, OSError):
340
341
342
343
344 pass
345 try:
346 os.rename(fname, self.sconsign)
347 except OSError:
348
349
350
351
352
353
354
355 open(self.sconsign, 'wb').write(open(fname, 'rb').read())
356 os.chmod(self.sconsign, mode)
357 try:
358 os.unlink(temp)
359 except (IOError, OSError):
360 pass
361
362 ForDirectory = DB
363
364 -def File(name, dbm_module=None):
365 """
366 Arrange for all signatures to be stored in a global .sconsign.db*
367 file.
368 """
369 global ForDirectory, DB_Name, DB_Module
370 if name is None:
371 ForDirectory = DirFile
372 DB_Module = None
373 else:
374 ForDirectory = DB
375 DB_Name = name
376 if not dbm_module is None:
377 DB_Module = dbm_module
378
379
380
381
382
383
384