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