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 from __future__ import print_function
31
32 __revision__ = "src/engine/SCons/SConsign.py 3a41ed6b288cee8d085373ad7fa02894e1903864 2019-01-23 17:30:35 bdeegan"
33
34 import SCons.compat
35
36 import os
37 import pickle
38
39 import SCons.dblite
40 import SCons.Warnings
41
42 from SCons.compat import PICKLE_PROTOCOL
43
44
48
49 SCons.dblite.ignore_corrupt_dbfiles = 1
50 SCons.dblite.corruption_warning = corrupt_dblite_warning
51
52
53 sig_files = []
54
55
56
57
58
59
60
61 DataBase = {}
62 DB_Module = SCons.dblite
63 DB_Name = ".sconsign"
64 DB_sync_list = []
65
66
68 global DataBase, DB_Module, DB_Name
69 top = dir.fs.Top
70 if not os.path.isabs(DB_Name) and top.repositories:
71 mode = "c"
72 for d in [top] + top.repositories:
73 if dir.is_under(d):
74 try:
75 return DataBase[d], mode
76 except KeyError:
77 path = d.entry_abspath(DB_Name)
78 try: db = DataBase[d] = DB_Module.open(path, mode)
79 except (IOError, OSError): pass
80 else:
81 if mode != "r":
82 DB_sync_list.append(db)
83 return db, mode
84 mode = "r"
85 try:
86 return DataBase[top], "c"
87 except KeyError:
88 db = DataBase[top] = DB_Module.open(DB_Name, "c")
89 DB_sync_list.append(db)
90 return db, "c"
91 except TypeError:
92 print("DataBase =", DataBase)
93 raise
94
95
102
103 normcase = os.path.normcase
104
105
107 global sig_files
108 for sig_file in sig_files:
109 sig_file.write(sync=0)
110 for db in DB_sync_list:
111 try:
112 syncmethod = db.sync
113 except AttributeError:
114 pass
115 else:
116 syncmethod()
117 try:
118 closemethod = db.close
119 except AttributeError:
120 pass
121 else:
122 closemethod()
123
124
125 -class SConsignEntry(object):
126 """
127 Wrapper class for the generic entry in a .sconsign file.
128 The Node subclass populates it with attributes as it pleases.
129
130 XXX As coded below, we do expect a '.binfo' attribute to be added,
131 but we'll probably generalize this in the next refactorings.
132 """
133 __slots__ = ("binfo", "ninfo", "__weakref__")
134 current_version_id = 2
135
136 - def __init__(self):
141
144
145 - def convert_from_sconsign(self, dir, name):
147
148 - def __getstate__(self):
149 state = getattr(self, '__dict__', {}).copy()
150 for obj in type(self).mro():
151 for name in getattr(obj,'__slots__',()):
152 if hasattr(self, name):
153 state[name] = getattr(self, name)
154
155 state['_version_id'] = self.current_version_id
156 try:
157 del state['__weakref__']
158 except KeyError:
159 pass
160 return state
161
162 - def __setstate__(self, state):
163 for key, value in state.items():
164 if key not in ('_version_id','__weakref__'):
165 setattr(self, key, value)
166
167
169 """
170 This is the controlling class for the signatures for the collection of
171 entries associated with a specific directory. The actual directory
172 association will be maintained by a subclass that is specific to
173 the underlying storage method. This class provides a common set of
174 methods for fetching and storing the individual bits of information
175 that make up signature entry.
176 """
178 self.entries = {}
179 self.dirty = False
180 self.to_be_merged = {}
181
182 - def get_entry(self, filename):
183 """
184 Fetch the specified entry attribute.
185 """
186 return self.entries[filename]
187
188 - def set_entry(self, filename, obj):
189 """
190 Set the entry.
191 """
192 self.entries[filename] = obj
193 self.dirty = True
194
195 - def do_not_set_entry(self, filename, obj):
197
203
206
221
222
224 """
225 A Base subclass that reads and writes signature information
226 from a global .sconsign.db* file--the actual file suffix is
227 determined by the database module.
228 """
268
269 - def write(self, sync=1):
294
295
314
315
317 """
318 Encapsulates reading and writing a per-directory .sconsign file.
319 """
343
344 - def write(self, sync=1):
345 """
346 Write the .sconsign file to disk.
347
348 Try to write to a temporary file first, and rename it if we
349 succeed. If we can't write to the temporary file, it's
350 probably because the directory isn't writable (and if so,
351 how did we build anything in this directory, anyway?), so
352 try to write directly to the .sconsign file as a backup.
353 If we can't rename, try to copy the temporary contents back
354 to the .sconsign file. Either way, always try to remove
355 the temporary file at the end.
356 """
357 if not self.dirty:
358 return
359
360 self.merge()
361
362 temp = os.path.join(self.dir.get_internal_path(), '.scons%d' % os.getpid())
363 try:
364 file = open(temp, 'wb')
365 fname = temp
366 except IOError:
367 try:
368 file = open(self.sconsign, 'wb')
369 fname = self.sconsign
370 except IOError:
371 return
372 for key, entry in self.entries.items():
373 entry.convert_to_sconsign()
374 pickle.dump(self.entries, file, PICKLE_PROTOCOL)
375 file.close()
376 if fname != self.sconsign:
377 try:
378 mode = os.stat(self.sconsign)[0]
379 os.chmod(self.sconsign, 0o666)
380 os.unlink(self.sconsign)
381 except (IOError, OSError):
382
383
384
385
386 pass
387 try:
388 os.rename(fname, self.sconsign)
389 except OSError:
390
391
392
393
394
395
396
397 open(self.sconsign, 'wb').write(open(fname, 'rb').read())
398 os.chmod(self.sconsign, mode)
399 try:
400 os.unlink(temp)
401 except (IOError, OSError):
402 pass
403
404 ForDirectory = DB
405
406
407 -def File(name, dbm_module=None):
408 """
409 Arrange for all signatures to be stored in a global .sconsign.db*
410 file.
411 """
412 global ForDirectory, DB_Name, DB_Module
413 if name is None:
414 ForDirectory = DirFile
415 DB_Module = None
416 else:
417 ForDirectory = DB
418 DB_Name = name
419 if not dbm_module is None:
420 DB_Module = dbm_module
421
422
423
424
425
426
427