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 rel_2.4.1:3453:73fefd3ea0b0 2015/11/09 03:25:05 bdbaddog"
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 __slots__ = ("binfo", "ninfo", "__weakref__")
126 current_version_id = 2
127
128 - def __init__(self):
133
136
137 - def convert_from_sconsign(self, dir, name):
139
140 - def __getstate__(self):
141 state = getattr(self, '__dict__', {}).copy()
142 for obj in type(self).mro():
143 for name in getattr(obj,'__slots__',()):
144 if hasattr(self, name):
145 state[name] = getattr(self, name)
146
147 state['_version_id'] = self.current_version_id
148 try:
149 del state['__weakref__']
150 except KeyError:
151 pass
152 return state
153
154 - def __setstate__(self, state):
155 for key, value in state.items():
156 if key not in ('_version_id','__weakref__'):
157 setattr(self, key, value)
158
160 """
161 This is the controlling class for the signatures for the collection of
162 entries associated with a specific directory. The actual directory
163 association will be maintained by a subclass that is specific to
164 the underlying storage method. This class provides a common set of
165 methods for fetching and storing the individual bits of information
166 that make up signature entry.
167 """
169 self.entries = {}
170 self.dirty = False
171 self.to_be_merged = {}
172
173 - def get_entry(self, filename):
174 """
175 Fetch the specified entry attribute.
176 """
177 return self.entries[filename]
178
179 - def set_entry(self, filename, obj):
180 """
181 Set the entry.
182 """
183 self.entries[filename] = obj
184 self.dirty = True
185
186 - def do_not_set_entry(self, filename, obj):
188
194
197
212
214 """
215 A Base subclass that reads and writes signature information
216 from a global .sconsign.db* file--the actual file suffix is
217 determined by the database module.
218 """
258
259 - def write(self, sync=1):
284
303
305 """
306 Encapsulates reading and writing a per-directory .sconsign file.
307 """
331
332 - def write(self, sync=1):
333 """
334 Write the .sconsign file to disk.
335
336 Try to write to a temporary file first, and rename it if we
337 succeed. If we can't write to the temporary file, it's
338 probably because the directory isn't writable (and if so,
339 how did we build anything in this directory, anyway?), so
340 try to write directly to the .sconsign file as a backup.
341 If we can't rename, try to copy the temporary contents back
342 to the .sconsign file. Either way, always try to remove
343 the temporary file at the end.
344 """
345 if not self.dirty:
346 return
347
348 self.merge()
349
350 temp = os.path.join(self.dir.get_internal_path(), '.scons%d' % os.getpid())
351 try:
352 file = open(temp, 'wb')
353 fname = temp
354 except IOError:
355 try:
356 file = open(self.sconsign, 'wb')
357 fname = self.sconsign
358 except IOError:
359 return
360 for key, entry in self.entries.items():
361 entry.convert_to_sconsign()
362 pickle.dump(self.entries, file, 1)
363 file.close()
364 if fname != self.sconsign:
365 try:
366 mode = os.stat(self.sconsign)[0]
367 os.chmod(self.sconsign, 0666)
368 os.unlink(self.sconsign)
369 except (IOError, OSError):
370
371
372
373
374 pass
375 try:
376 os.rename(fname, self.sconsign)
377 except OSError:
378
379
380
381
382
383
384
385 open(self.sconsign, 'wb').write(open(fname, 'rb').read())
386 os.chmod(self.sconsign, mode)
387 try:
388 os.unlink(temp)
389 except (IOError, OSError):
390 pass
391
392 ForDirectory = DB
393
394 -def File(name, dbm_module=None):
395 """
396 Arrange for all signatures to be stored in a global .sconsign.db*
397 file.
398 """
399 global ForDirectory, DB_Name, DB_Module
400 if name is None:
401 ForDirectory = DirFile
402 DB_Module = None
403 else:
404 ForDirectory = DB
405 DB_Name = name
406 if not dbm_module is None:
407 DB_Module = dbm_module
408
409
410
411
412
413
414