3 Writing and reading information to the .sconsign file or files.
10 # Permission is hereby granted, free of charge, to any person obtaining
11 # a copy of this software and associated documentation files (the
12 # "Software"), to deal in the Software without restriction, including
13 # without limitation the rights to use, copy, modify, merge, publish,
14 # distribute, sublicense, and/or sell copies of the Software, and to
15 # permit persons to whom the Software is furnished to do so, subject to
16 # the following conditions:
18 # The above copyright notice and this permission notice shall be included
19 # in all copies or substantial portions of the Software.
21 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
22 # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
23 # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
25 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
26 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
27 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
43 def corrupt_dblite_warning(filename):
44 SCons.Warnings.warn(SCons.Warnings.CorruptSConsignWarning,
45 "Ignoring corrupt .sconsign file: %s"%filename)
47 SCons.dblite.ignore_corrupt_dbfiles = 1
48 SCons.dblite.corruption_warning = corrupt_dblite_warning
50 #XXX Get rid of the global array so this becomes re-entrant.
53 # Info for the database SConsign implementation (now the default):
54 # "DataBase" is a dictionary that maps top-level SConstruct directories
55 # to open database handles.
56 # "DB_Module" is the Python database module to create the handles.
57 # "DB_Name" is the base name of the database file (minus any
58 # extension the underlying DB module will add).
60 DB_Module = SCons.dblite
64 def Get_DataBase(dir):
65 global DataBase, DB_Module, DB_Name
67 if not os.path.isabs(DB_Name) and top.repositories:
69 for d in [top] + top.repositories:
72 return DataBase[d], mode
74 path = d.entry_abspath(DB_Name)
75 try: db = DataBase[d] = DB_Module.open(path, mode)
76 except (IOError, OSError): pass
79 DB_sync_list.append(db)
83 return DataBase[top], "c"
85 db = DataBase[top] = DB_Module.open(DB_Name, "c")
86 DB_sync_list.append(db)
89 print "DataBase =", DataBase
93 """Reset global state. Used by unit tests that end up using
94 SConsign multiple times to get a clean slate for each test."""
95 global sig_files, DB_sync_list
100 norm_entry = lambda s: s
103 return string.replace(str, os.sep, '/')
107 for sig_file in sig_files:
108 sig_file.write(sync=0)
109 for db in DB_sync_list:
112 except AttributeError:
113 pass # Not all anydbm modules have sync() methods.
119 This is the controlling class for the signatures for the collection of
120 entries associated with a specific directory. The actual directory
121 association will be maintained by a subclass that is specific to
122 the underlying storage method. This class provides a common set of
123 methods for fetching and storing the individual bits of information
124 that make up signature entry.
126 def __init__(self, module=None):
128 module - the signature module being used
131 self.module = module or SCons.Sig.default_calc.module
135 def get_entry(self, filename):
137 Fetch the specified entry attribute.
139 return self.entries[filename]
141 def set_entry(self, filename, obj):
145 self.entries[filename] = obj
148 def do_not_set_entry(self, filename, obj):
153 A Base subclass that reads and writes signature information
154 from a global .sconsign.db* file--the actual file suffix is
155 determined by the specified database module.
157 def __init__(self, dir, module=None):
158 Base.__init__(self, module)
162 db, mode = Get_DataBase(dir)
164 # Read using the path relative to the top of the Repository
165 # (self.dir.tpath) from which we're fetching the signature
167 path = norm_entry(dir.tpath)
169 rawentries = db[path]
174 self.entries = cPickle.loads(rawentries)
175 if type(self.entries) is not type({}):
178 except KeyboardInterrupt:
181 SCons.Warnings.warn(SCons.Warnings.CorruptSConsignWarning,
182 "Ignoring corrupt sconsign entry : %s (%s)\n"%(self.dir.tpath, e))
183 for key, entry in self.entries.items():
184 entry.convert_from_sconsign(dir, key)
187 # This directory is actually under a repository, which means
188 # likely they're reaching in directly for a dependency on
189 # a file there. Don't actually set any entry info, so we
190 # won't try to write to that .sconsign.dblite file.
191 self.set_entry = self.do_not_set_entry
194 sig_files.append(self)
196 def write(self, sync=1):
200 db, mode = Get_DataBase(self.dir)
202 # Write using the path relative to the top of the SConstruct
203 # directory (self.dir.path), not relative to the top of
204 # the Repository; we only write to our own .sconsign file,
205 # not to .sconsign files in Repositories.
206 path = norm_entry(self.dir.path)
207 for key, entry in self.entries.items():
208 entry.convert_to_sconsign()
209 db[path] = cPickle.dumps(self.entries, 1)
214 except AttributeError:
215 # Not all anydbm modules have sync() methods.
221 def __init__(self, fp=None, module=None):
223 fp - file pointer to read entries from
224 module - the signature module being used
226 Base.__init__(self, module)
229 self.entries = cPickle.load(fp)
230 if type(self.entries) is not type({}):
236 Encapsulates reading and writing a per-directory .sconsign file.
238 def __init__(self, dir, module=None):
240 dir - the directory for the file
241 module - the signature module being used
245 self.sconsign = os.path.join(dir.path, '.sconsign')
248 fp = open(self.sconsign, 'rb')
253 Dir.__init__(self, fp, module)
254 except KeyboardInterrupt:
257 SCons.Warnings.warn(SCons.Warnings.CorruptSConsignWarning,
258 "Ignoring corrupt .sconsign file: %s"%self.sconsign)
261 sig_files.append(self)
263 def get_entry(self, filename):
265 Fetch the specified entry attribute, converting from .sconsign
266 format to in-memory format.
268 entry = Dir.get_entry(self, filename)
269 entry.convert_from_sconsign(self.dir, filename)
272 def write(self, sync=1):
274 Write the .sconsign file to disk.
276 Try to write to a temporary file first, and rename it if we
277 succeed. If we can't write to the temporary file, it's
278 probably because the directory isn't writable (and if so,
279 how did we build anything in this directory, anyway?), so
280 try to write directly to the .sconsign file as a backup.
281 If we can't rename, try to copy the temporary contents back
282 to the .sconsign file. Either way, always try to remove
283 the temporary file at the end.
286 temp = os.path.join(self.dir.path, '.scons%d' % os.getpid())
288 file = open(temp, 'wb')
292 file = open(self.sconsign, 'wb')
293 fname = self.sconsign
296 for key, entry in self.entries.items():
297 entry.convert_to_sconsign()
298 cPickle.dump(self.entries, file, 1)
300 if fname != self.sconsign:
302 mode = os.stat(self.sconsign)[0]
303 os.chmod(self.sconsign, 0666)
304 os.unlink(self.sconsign)
308 os.rename(fname, self.sconsign)
310 open(self.sconsign, 'wb').write(open(fname, 'rb').read())
311 os.chmod(self.sconsign, mode)
319 def File(name, dbm_module=None):
321 Arrange for all signatures to be stored in a global .sconsign.db*
324 global ForDirectory, DB_Name, DB_Module
326 ForDirectory = DirFile
331 if not dbm_module is None:
332 DB_Module = dbm_module