| Index: scons-2.0.1/engine/SCons/SConsign.py
|
| ===================================================================
|
| --- scons-2.0.1/engine/SCons/SConsign.py (revision 0)
|
| +++ scons-2.0.1/engine/SCons/SConsign.py (revision 0)
|
| @@ -0,0 +1,383 @@
|
| +"""SCons.SConsign
|
| +
|
| +Writing and reading information to the .sconsign file or files.
|
| +
|
| +"""
|
| +
|
| +#
|
| +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 The SCons Foundation
|
| +#
|
| +# Permission is hereby granted, free of charge, to any person obtaining
|
| +# a copy of this software and associated documentation files (the
|
| +# "Software"), to deal in the Software without restriction, including
|
| +# without limitation the rights to use, copy, modify, merge, publish,
|
| +# distribute, sublicense, and/or sell copies of the Software, and to
|
| +# permit persons to whom the Software is furnished to do so, subject to
|
| +# the following conditions:
|
| +#
|
| +# The above copyright notice and this permission notice shall be included
|
| +# in all copies or substantial portions of the Software.
|
| +#
|
| +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
|
| +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
| +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
| +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
| +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
| +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
| +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
| +#
|
| +
|
| +__revision__ = "src/engine/SCons/SConsign.py 5134 2010/08/16 23:02:40 bdeegan"
|
| +
|
| +import SCons.compat
|
| +
|
| +import os
|
| +# compat layer imports "cPickle" for us if it's available.
|
| +import pickle
|
| +
|
| +import SCons.dblite
|
| +import SCons.Warnings
|
| +
|
| +def corrupt_dblite_warning(filename):
|
| + SCons.Warnings.warn(SCons.Warnings.CorruptSConsignWarning,
|
| + "Ignoring corrupt .sconsign file: %s"%filename)
|
| +
|
| +SCons.dblite.ignore_corrupt_dbfiles = 1
|
| +SCons.dblite.corruption_warning = corrupt_dblite_warning
|
| +
|
| +#XXX Get rid of the global array so this becomes re-entrant.
|
| +sig_files = []
|
| +
|
| +# Info for the database SConsign implementation (now the default):
|
| +# "DataBase" is a dictionary that maps top-level SConstruct directories
|
| +# to open database handles.
|
| +# "DB_Module" is the Python database module to create the handles.
|
| +# "DB_Name" is the base name of the database file (minus any
|
| +# extension the underlying DB module will add).
|
| +DataBase = {}
|
| +DB_Module = SCons.dblite
|
| +DB_Name = ".sconsign"
|
| +DB_sync_list = []
|
| +
|
| +def Get_DataBase(dir):
|
| + global DataBase, DB_Module, DB_Name
|
| + top = dir.fs.Top
|
| + if not os.path.isabs(DB_Name) and top.repositories:
|
| + mode = "c"
|
| + for d in [top] + top.repositories:
|
| + if dir.is_under(d):
|
| + try:
|
| + return DataBase[d], mode
|
| + except KeyError:
|
| + path = d.entry_abspath(DB_Name)
|
| + try: db = DataBase[d] = DB_Module.open(path, mode)
|
| + except (IOError, OSError): pass
|
| + else:
|
| + if mode != "r":
|
| + DB_sync_list.append(db)
|
| + return db, mode
|
| + mode = "r"
|
| + try:
|
| + return DataBase[top], "c"
|
| + except KeyError:
|
| + db = DataBase[top] = DB_Module.open(DB_Name, "c")
|
| + DB_sync_list.append(db)
|
| + return db, "c"
|
| + except TypeError:
|
| + print "DataBase =", DataBase
|
| + raise
|
| +
|
| +def Reset():
|
| + """Reset global state. Used by unit tests that end up using
|
| + SConsign multiple times to get a clean slate for each test."""
|
| + global sig_files, DB_sync_list
|
| + sig_files = []
|
| + DB_sync_list = []
|
| +
|
| +normcase = os.path.normcase
|
| +
|
| +def write():
|
| + global sig_files
|
| + for sig_file in sig_files:
|
| + sig_file.write(sync=0)
|
| + for db in DB_sync_list:
|
| + try:
|
| + syncmethod = db.sync
|
| + except AttributeError:
|
| + pass # Not all anydbm modules have sync() methods.
|
| + else:
|
| + syncmethod()
|
| +
|
| +class SConsignEntry(object):
|
| + """
|
| + Wrapper class for the generic entry in a .sconsign file.
|
| + The Node subclass populates it with attributes as it pleases.
|
| +
|
| + XXX As coded below, we do expect a '.binfo' attribute to be added,
|
| + but we'll probably generalize this in the next refactorings.
|
| + """
|
| + current_version_id = 1
|
| + def __init__(self):
|
| + # Create an object attribute from the class attribute so it ends up
|
| + # in the pickled data in the .sconsign file.
|
| + _version_id = self.current_version_id
|
| + def convert_to_sconsign(self):
|
| + self.binfo.convert_to_sconsign()
|
| + def convert_from_sconsign(self, dir, name):
|
| + self.binfo.convert_from_sconsign(dir, name)
|
| +
|
| +class Base(object):
|
| + """
|
| + This is the controlling class for the signatures for the collection of
|
| + entries associated with a specific directory. The actual directory
|
| + association will be maintained by a subclass that is specific to
|
| + the underlying storage method. This class provides a common set of
|
| + methods for fetching and storing the individual bits of information
|
| + that make up signature entry.
|
| + """
|
| + def __init__(self):
|
| + self.entries = {}
|
| + self.dirty = False
|
| + self.to_be_merged = {}
|
| +
|
| + def get_entry(self, filename):
|
| + """
|
| + Fetch the specified entry attribute.
|
| + """
|
| + return self.entries[filename]
|
| +
|
| + def set_entry(self, filename, obj):
|
| + """
|
| + Set the entry.
|
| + """
|
| + self.entries[filename] = obj
|
| + self.dirty = True
|
| +
|
| + def do_not_set_entry(self, filename, obj):
|
| + pass
|
| +
|
| + def store_info(self, filename, node):
|
| + entry = node.get_stored_info()
|
| + entry.binfo.merge(node.get_binfo())
|
| + self.to_be_merged[filename] = node
|
| + self.dirty = True
|
| +
|
| + def do_not_store_info(self, filename, node):
|
| + pass
|
| +
|
| + def merge(self):
|
| + for key, node in self.to_be_merged.items():
|
| + entry = node.get_stored_info()
|
| + try:
|
| + ninfo = entry.ninfo
|
| + except AttributeError:
|
| + # This happens with SConf Nodes, because the configuration
|
| + # subsystem takes direct control over how the build decision
|
| + # is made and its information stored.
|
| + pass
|
| + else:
|
| + ninfo.merge(node.get_ninfo())
|
| + self.entries[key] = entry
|
| + self.to_be_merged = {}
|
| +
|
| +class DB(Base):
|
| + """
|
| + A Base subclass that reads and writes signature information
|
| + from a global .sconsign.db* file--the actual file suffix is
|
| + determined by the database module.
|
| + """
|
| + def __init__(self, dir):
|
| + Base.__init__(self)
|
| +
|
| + self.dir = dir
|
| +
|
| + db, mode = Get_DataBase(dir)
|
| +
|
| + # Read using the path relative to the top of the Repository
|
| + # (self.dir.tpath) from which we're fetching the signature
|
| + # information.
|
| + path = normcase(dir.tpath)
|
| + try:
|
| + rawentries = db[path]
|
| + except KeyError:
|
| + pass
|
| + else:
|
| + try:
|
| + self.entries = pickle.loads(rawentries)
|
| + if not isinstance(self.entries, dict):
|
| + self.entries = {}
|
| + raise TypeError
|
| + except KeyboardInterrupt:
|
| + raise
|
| + except Exception, e:
|
| + SCons.Warnings.warn(SCons.Warnings.CorruptSConsignWarning,
|
| + "Ignoring corrupt sconsign entry : %s (%s)\n"%(self.dir.tpath, e))
|
| + for key, entry in self.entries.items():
|
| + entry.convert_from_sconsign(dir, key)
|
| +
|
| + if mode == "r":
|
| + # This directory is actually under a repository, which means
|
| + # likely they're reaching in directly for a dependency on
|
| + # a file there. Don't actually set any entry info, so we
|
| + # won't try to write to that .sconsign.dblite file.
|
| + self.set_entry = self.do_not_set_entry
|
| + self.store_info = self.do_not_store_info
|
| +
|
| + global sig_files
|
| + sig_files.append(self)
|
| +
|
| + def write(self, sync=1):
|
| + if not self.dirty:
|
| + return
|
| +
|
| + self.merge()
|
| +
|
| + db, mode = Get_DataBase(self.dir)
|
| +
|
| + # Write using the path relative to the top of the SConstruct
|
| + # directory (self.dir.path), not relative to the top of
|
| + # the Repository; we only write to our own .sconsign file,
|
| + # not to .sconsign files in Repositories.
|
| + path = normcase(self.dir.path)
|
| + for key, entry in self.entries.items():
|
| + entry.convert_to_sconsign()
|
| + db[path] = pickle.dumps(self.entries, 1)
|
| +
|
| + if sync:
|
| + try:
|
| + syncmethod = db.sync
|
| + except AttributeError:
|
| + # Not all anydbm modules have sync() methods.
|
| + pass
|
| + else:
|
| + syncmethod()
|
| +
|
| +class Dir(Base):
|
| + def __init__(self, fp=None, dir=None):
|
| + """
|
| + fp - file pointer to read entries from
|
| + """
|
| + Base.__init__(self)
|
| +
|
| + if not fp:
|
| + return
|
| +
|
| + self.entries = pickle.load(fp)
|
| + if not isinstance(self.entries, dict):
|
| + self.entries = {}
|
| + raise TypeError
|
| +
|
| + if dir:
|
| + for key, entry in self.entries.items():
|
| + entry.convert_from_sconsign(dir, key)
|
| +
|
| +class DirFile(Dir):
|
| + """
|
| + Encapsulates reading and writing a per-directory .sconsign file.
|
| + """
|
| + def __init__(self, dir):
|
| + """
|
| + dir - the directory for the file
|
| + """
|
| +
|
| + self.dir = dir
|
| + self.sconsign = os.path.join(dir.path, '.sconsign')
|
| +
|
| + try:
|
| + fp = open(self.sconsign, 'rb')
|
| + except IOError:
|
| + fp = None
|
| +
|
| + try:
|
| + Dir.__init__(self, fp, dir)
|
| + except KeyboardInterrupt:
|
| + raise
|
| + except:
|
| + SCons.Warnings.warn(SCons.Warnings.CorruptSConsignWarning,
|
| + "Ignoring corrupt .sconsign file: %s"%self.sconsign)
|
| +
|
| + global sig_files
|
| + sig_files.append(self)
|
| +
|
| + def write(self, sync=1):
|
| + """
|
| + Write the .sconsign file to disk.
|
| +
|
| + Try to write to a temporary file first, and rename it if we
|
| + succeed. If we can't write to the temporary file, it's
|
| + probably because the directory isn't writable (and if so,
|
| + how did we build anything in this directory, anyway?), so
|
| + try to write directly to the .sconsign file as a backup.
|
| + If we can't rename, try to copy the temporary contents back
|
| + to the .sconsign file. Either way, always try to remove
|
| + the temporary file at the end.
|
| + """
|
| + if not self.dirty:
|
| + return
|
| +
|
| + self.merge()
|
| +
|
| + temp = os.path.join(self.dir.path, '.scons%d' % os.getpid())
|
| + try:
|
| + file = open(temp, 'wb')
|
| + fname = temp
|
| + except IOError:
|
| + try:
|
| + file = open(self.sconsign, 'wb')
|
| + fname = self.sconsign
|
| + except IOError:
|
| + return
|
| + for key, entry in self.entries.items():
|
| + entry.convert_to_sconsign()
|
| + pickle.dump(self.entries, file, 1)
|
| + file.close()
|
| + if fname != self.sconsign:
|
| + try:
|
| + mode = os.stat(self.sconsign)[0]
|
| + os.chmod(self.sconsign, 0666)
|
| + os.unlink(self.sconsign)
|
| + except (IOError, OSError):
|
| + # Try to carry on in the face of either OSError
|
| + # (things like permission issues) or IOError (disk
|
| + # or network issues). If there's a really dangerous
|
| + # issue, it should get re-raised by the calls below.
|
| + pass
|
| + try:
|
| + os.rename(fname, self.sconsign)
|
| + except OSError:
|
| + # An OSError failure to rename may indicate something
|
| + # like the directory has no write permission, but
|
| + # the .sconsign file itself might still be writable,
|
| + # so try writing on top of it directly. An IOError
|
| + # here, or in any of the following calls, would get
|
| + # raised, indicating something like a potentially
|
| + # serious disk or network issue.
|
| + open(self.sconsign, 'wb').write(open(fname, 'rb').read())
|
| + os.chmod(self.sconsign, mode)
|
| + try:
|
| + os.unlink(temp)
|
| + except (IOError, OSError):
|
| + pass
|
| +
|
| +ForDirectory = DB
|
| +
|
| +def File(name, dbm_module=None):
|
| + """
|
| + Arrange for all signatures to be stored in a global .sconsign.db*
|
| + file.
|
| + """
|
| + global ForDirectory, DB_Name, DB_Module
|
| + if name is None:
|
| + ForDirectory = DirFile
|
| + DB_Module = None
|
| + else:
|
| + ForDirectory = DB
|
| + DB_Name = name
|
| + if not dbm_module is None:
|
| + DB_Module = dbm_module
|
| +
|
| +# Local Variables:
|
| +# tab-width:4
|
| +# indent-tabs-mode:nil
|
| +# End:
|
| +# vim: set expandtab tabstop=4 shiftwidth=4:
|
|
|
| Property changes on: scons-2.0.1/engine/SCons/SConsign.py
|
| ___________________________________________________________________
|
| Added: svn:eol-style
|
| + LF
|
|
|
|
|