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 e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 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):
80 pass
81 else:
82 if mode != "r":
83 DB_sync_list.append(db)
84 return db, mode
85 mode = "r"
86 try:
87 return DataBase[top], "c"
88 except KeyError:
89 db = DataBase[top] = DB_Module.open(DB_Name, "c")
90 DB_sync_list.append(db)
91 return db, "c"
92 except TypeError:
93 print("DataBase =", DataBase)
94 raise
95
96
103
104 normcase = os.path.normcase
105
106
108 global sig_files
109 for sig_file in sig_files:
110 sig_file.write(sync=0)
111 for db in DB_sync_list:
112 try:
113 syncmethod = db.sync
114 except AttributeError:
115 pass
116 else:
117 syncmethod()
118 try:
119 closemethod = db.close
120 except AttributeError:
121 pass
122 else:
123 closemethod()
124
125
126 -class SConsignEntry(object):
127 """
128 Wrapper class for the generic entry in a .sconsign file.
129 The Node subclass populates it with attributes as it pleases.
130
131 XXX As coded below, we do expect a '.binfo' attribute to be added,
132 but we'll probably generalize this in the next refactorings.
133 """
134 __slots__ = ("binfo", "ninfo", "__weakref__")
135 current_version_id = 2
136
137 - def __init__(self):
142
145
146 - def convert_from_sconsign(self, dir, name):
148
149 - def __getstate__(self):
150 state = getattr(self, '__dict__', {}).copy()
151 for obj in type(self).mro():
152 for name in getattr(obj,'__slots__',()):
153 if hasattr(self, name):
154 state[name] = getattr(self, name)
155
156 state['_version_id'] = self.current_version_id
157 try:
158 del state['__weakref__']
159 except KeyError:
160 pass
161 return state
162
163 - def __setstate__(self, state):
164 for key, value in state.items():
165 if key not in ('_version_id','__weakref__'):
166 setattr(self, key, value)
167
168
170 """
171 This is the controlling class for the signatures for the collection of
172 entries associated with a specific directory. The actual directory
173 association will be maintained by a subclass that is specific to
174 the underlying storage method. This class provides a common set of
175 methods for fetching and storing the individual bits of information
176 that make up signature entry.
177 """
179 self.entries = {}
180 self.dirty = False
181 self.to_be_merged = {}
182
183 - def get_entry(self, filename):
184 """
185 Fetch the specified entry attribute.
186 """
187 return self.entries[filename]
188
189 - def set_entry(self, filename, obj):
190 """
191 Set the entry.
192 """
193 self.entries[filename] = obj
194 self.dirty = True
195
196 - def do_not_set_entry(self, filename, obj):
198
204
207
222
223
225 """
226 A Base subclass that reads and writes signature information
227 from a global .sconsign.db* file--the actual file suffix is
228 determined by the database module.
229 """
269
270 - def write(self, sync=1):
295
296
315
316
318 """
319 Encapsulates reading and writing a per-directory .sconsign file.
320 """
349
350 - def write(self, sync=1):
351 """
352 Write the .sconsign file to disk.
353
354 Try to write to a temporary file first, and rename it if we
355 succeed. If we can't write to the temporary file, it's
356 probably because the directory isn't writable (and if so,
357 how did we build anything in this directory, anyway?), so
358 try to write directly to the .sconsign file as a backup.
359 If we can't rename, try to copy the temporary contents back
360 to the .sconsign file. Either way, always try to remove
361 the temporary file at the end.
362 """
363 if not self.dirty:
364 return
365
366 self.merge()
367
368 temp = os.path.join(self.dir.get_internal_path(), '.scons%d' % os.getpid())
369 try:
370 file = open(temp, 'wb')
371 fname = temp
372 except IOError:
373 try:
374 file = open(self.sconsign, 'wb')
375 fname = self.sconsign
376 except IOError:
377 return
378 for key, entry in self.entries.items():
379 entry.convert_to_sconsign()
380 pickle.dump(self.entries, file, PICKLE_PROTOCOL)
381 file.close()
382 if fname != self.sconsign:
383 try:
384 mode = os.stat(self.sconsign)[0]
385 os.chmod(self.sconsign, 0o666)
386 os.unlink(self.sconsign)
387 except (IOError, OSError):
388
389
390
391
392 pass
393 try:
394 os.rename(fname, self.sconsign)
395 except OSError:
396
397
398
399
400
401
402
403 with open(self.sconsign, 'wb') as f, open(fname, 'rb') as f2:
404 f.write(f2.read())
405 os.chmod(self.sconsign, mode)
406 try:
407 os.unlink(temp)
408 except (IOError, OSError):
409 pass
410
411 ForDirectory = DB
412
413
414 -def File(name, dbm_module=None):
415 """
416 Arrange for all signatures to be stored in a global .sconsign.db*
417 file.
418 """
419 global ForDirectory, DB_Name, DB_Module
420 if name is None:
421 ForDirectory = DirFile
422 DB_Module = None
423 else:
424 ForDirectory = DB
425 DB_Name = name
426 if dbm_module is not None:
427 DB_Module = dbm_module
428
429
430
431
432
433
434