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 2014/09/27 12:51:43 garyo"
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
100 global sig_files
101 for sig_file in sig_files:
102 sig_file.write(sync=0)
103 for db in DB_sync_list:
104 try:
105 syncmethod = db.sync
106 except AttributeError:
107 pass
108 else:
109 syncmethod()
110 try:
111 closemethod = db.close
112 except AttributeError:
113 pass
114 else:
115 closemethod()
116
117 -class SConsignEntry(object):
118 """
119 Wrapper class for the generic entry in a .sconsign file.
120 The Node subclass populates it with attributes as it pleases.
121
122 XXX As coded below, we do expect a '.binfo' attribute to be added,
123 but we'll probably generalize this in the next refactorings.
124 """
125 current_version_id = 1
126 - def __init__(self):
127
128
129 _version_id = self.current_version_id
132 - def convert_from_sconsign(self, dir, name):
133 self.binfo.convert_from_sconsign(dir, name)
134
136 """
137 This is the controlling class for the signatures for the collection of
138 entries associated with a specific directory. The actual directory
139 association will be maintained by a subclass that is specific to
140 the underlying storage method. This class provides a common set of
141 methods for fetching and storing the individual bits of information
142 that make up signature entry.
143 """
145 self.entries = {}
146 self.dirty = False
147 self.to_be_merged = {}
148
149 - def get_entry(self, filename):
150 """
151 Fetch the specified entry attribute.
152 """
153 return self.entries[filename]
154
155 - def set_entry(self, filename, obj):
156 """
157 Set the entry.
158 """
159 self.entries[filename] = obj
160 self.dirty = True
161
162 - def do_not_set_entry(self, filename, obj):
164
170
173
175 for key, node in self.to_be_merged.items():
176 entry = node.get_stored_info()
177 try:
178 ninfo = entry.ninfo
179 except AttributeError:
180
181
182
183 pass
184 else:
185 ninfo.merge(node.get_ninfo())
186 self.entries[key] = entry
187 self.to_be_merged = {}
188
190 """
191 A Base subclass that reads and writes signature information
192 from a global .sconsign.db* file--the actual file suffix is
193 determined by the database module.
194 """
234
235 - def write(self, sync=1):
236 if not self.dirty:
237 return
238
239 self.merge()
240
241 db, mode = Get_DataBase(self.dir)
242
243
244
245
246
247 path = normcase(self.dir.path)
248 for key, entry in self.entries.items():
249 entry.convert_to_sconsign()
250 db[path] = pickle.dumps(self.entries, 1)
251
252 if sync:
253 try:
254 syncmethod = db.sync
255 except AttributeError:
256
257 pass
258 else:
259 syncmethod()
260
263 """
264 fp - file pointer to read entries from
265 """
266 Base.__init__(self)
267
268 if not fp:
269 return
270
271 self.entries = pickle.load(fp)
272 if not isinstance(self.entries, dict):
273 self.entries = {}
274 raise TypeError
275
276 if dir:
277 for key, entry in self.entries.items():
278 entry.convert_from_sconsign(dir, key)
279
281 """
282 Encapsulates reading and writing a per-directory .sconsign file.
283 """
307
308 - def write(self, sync=1):
309 """
310 Write the .sconsign file to disk.
311
312 Try to write to a temporary file first, and rename it if we
313 succeed. If we can't write to the temporary file, it's
314 probably because the directory isn't writable (and if so,
315 how did we build anything in this directory, anyway?), so
316 try to write directly to the .sconsign file as a backup.
317 If we can't rename, try to copy the temporary contents back
318 to the .sconsign file. Either way, always try to remove
319 the temporary file at the end.
320 """
321 if not self.dirty:
322 return
323
324 self.merge()
325
326 temp = os.path.join(self.dir.path, '.scons%d' % os.getpid())
327 try:
328 file = open(temp, 'wb')
329 fname = temp
330 except IOError:
331 try:
332 file = open(self.sconsign, 'wb')
333 fname = self.sconsign
334 except IOError:
335 return
336 for key, entry in self.entries.items():
337 entry.convert_to_sconsign()
338 pickle.dump(self.entries, file, 1)
339 file.close()
340 if fname != self.sconsign:
341 try:
342 mode = os.stat(self.sconsign)[0]
343 os.chmod(self.sconsign, 0666)
344 os.unlink(self.sconsign)
345 except (IOError, OSError):
346
347
348
349
350 pass
351 try:
352 os.rename(fname, self.sconsign)
353 except OSError:
354
355
356
357
358
359
360
361 open(self.sconsign, 'wb').write(open(fname, 'rb').read())
362 os.chmod(self.sconsign, mode)
363 try:
364 os.unlink(temp)
365 except (IOError, OSError):
366 pass
367
368 ForDirectory = DB
369
370 -def File(name, dbm_module=None):
371 """
372 Arrange for all signatures to be stored in a global .sconsign.db*
373 file.
374 """
375 global ForDirectory, DB_Name, DB_Module
376 if name is None:
377 ForDirectory = DirFile
378 DB_Module = None
379 else:
380 ForDirectory = DB
381 DB_Name = name
382 if not dbm_module is None:
383 DB_Module = dbm_module
384
385
386
387
388
389
390