Package SCons :: Module SConsign
[hide private]
[frames] | no frames]

Source Code for Module SCons.SConsign

  1  """SCons.SConsign 
  2   
  3  Writing and reading information to the .sconsign file or files. 
  4   
  5  """ 
  6   
  7  # 
  8  # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation 
  9  # 
 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: 
 17  # 
 18  # The above copyright notice and this permission notice shall be included 
 19  # in all copies or substantial portions of the Software. 
 20  # 
 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. 
 28  # 
 29   
 30  __revision__ = "src/engine/SCons/SConsign.py 2928 2008/04/29 22:44:09 knight" 
 31   
 32  import SCons.compat 
 33   
 34  import cPickle 
 35  import os 
 36  import os.path 
 37   
 38  import SCons.dblite 
 39  import SCons.Warnings 
 40   
41 -def corrupt_dblite_warning(filename):
42 SCons.Warnings.warn(SCons.Warnings.CorruptSConsignWarning, 43 "Ignoring corrupt .sconsign file: %s"%filename)
44 45 SCons.dblite.ignore_corrupt_dbfiles = 1 46 SCons.dblite.corruption_warning = corrupt_dblite_warning 47 48 #XXX Get rid of the global array so this becomes re-entrant. 49 sig_files = [] 50 51 # Info for the database SConsign implementation (now the default): 52 # "DataBase" is a dictionary that maps top-level SConstruct directories 53 # to open database handles. 54 # "DB_Module" is the Python database module to create the handles. 55 # "DB_Name" is the base name of the database file (minus any 56 # extension the underlying DB module will add). 57 DataBase = {} 58 DB_Module = SCons.dblite 59 DB_Name = ".sconsign" 60 DB_sync_list = [] 61
62 -def Get_DataBase(dir):
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
90 -def Reset():
91 """Reset global state. Used by unit tests that end up using 92 SConsign multiple times to get a clean slate for each test.""" 93 global sig_files, DB_sync_list 94 sig_files = [] 95 DB_sync_list = []
96 97 normcase = os.path.normcase 98
99 -def write():
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 # Not all anydbm modules have sync() methods. 108 else: 109 syncmethod()
110
111 -class SConsignEntry:
112 """ 113 Wrapper class for the generic entry in a .sconsign file. 114 The Node subclass populates it with attributes as it pleases. 115 116 XXX As coded below, we do expect a '.binfo' attribute to be added, 117 but we'll probably generalize this in the next refactorings. 118 """ 119 current_version_id = 1
120 - def __init__(self):
121 # Create an object attribute from the class attribute so it ends up 122 # in the pickled data in the .sconsign file. 123 _version_id = self.current_version_id
124 - def convert_to_sconsign(self):
125 self.binfo.convert_to_sconsign()
126 - def convert_from_sconsign(self, dir, name):
127 self.binfo.convert_from_sconsign(dir, name)
128
129 -class Base:
130 """ 131 This is the controlling class for the signatures for the collection of 132 entries associated with a specific directory. The actual directory 133 association will be maintained by a subclass that is specific to 134 the underlying storage method. This class provides a common set of 135 methods for fetching and storing the individual bits of information 136 that make up signature entry. 137 """
138 - def __init__(self):
139 self.entries = {} 140 self.dirty = False 141 self.to_be_merged = {}
142
143 - def get_entry(self, filename):
144 """ 145 Fetch the specified entry attribute. 146 """ 147 return self.entries[filename]
148
149 - def set_entry(self, filename, obj):
150 """ 151 Set the entry. 152 """ 153 self.entries[filename] = obj 154 self.dirty = True
155
156 - def do_not_set_entry(self, filename, obj):
157 pass
158
159 - def store_info(self, filename, node):
160 entry = node.get_stored_info() 161 entry.binfo.merge(node.get_binfo()) 162 self.to_be_merged[filename] = node 163 self.dirty = True
164
165 - def do_not_store_info(self, filename, node):
166 pass
167
168 - def merge(self):
169 for key, node in self.to_be_merged.items(): 170 entry = node.get_stored_info() 171 try: 172 ninfo = entry.ninfo 173 except AttributeError: 174 # This happens with SConf Nodes, because the configuration 175 # subsystem takes direct control over how the build decision 176 # is made and its information stored. 177 pass 178 else: 179 ninfo.merge(node.get_ninfo()) 180 self.entries[key] = entry 181 self.to_be_merged = {}
182
183 -class DB(Base):
184 """ 185 A Base subclass that reads and writes signature information 186 from a global .sconsign.db* file--the actual file suffix is 187 determined by the database module. 188 """
189 - def __init__(self, dir):
190 Base.__init__(self) 191 192 self.dir = dir 193 194 db, mode = Get_DataBase(dir) 195 196 # Read using the path relative to the top of the Repository 197 # (self.dir.tpath) from which we're fetching the signature 198 # information. 199 path = normcase(dir.tpath) 200 try: 201 rawentries = db[path] 202 except KeyError: 203 pass 204 else: 205 try: 206 self.entries = cPickle.loads(rawentries) 207 if type(self.entries) is not type({}): 208 self.entries = {} 209 raise TypeError 210 except KeyboardInterrupt: 211 raise 212 except Exception, e: 213 SCons.Warnings.warn(SCons.Warnings.CorruptSConsignWarning, 214 "Ignoring corrupt sconsign entry : %s (%s)\n"%(self.dir.tpath, e)) 215 for key, entry in self.entries.items(): 216 entry.convert_from_sconsign(dir, key) 217 218 if mode == "r": 219 # This directory is actually under a repository, which means 220 # likely they're reaching in directly for a dependency on 221 # a file there. Don't actually set any entry info, so we 222 # won't try to write to that .sconsign.dblite file. 223 self.set_entry = self.do_not_set_entry 224 self.store_info = self.do_not_store_info 225 226 global sig_files 227 sig_files.append(self)
228
229 - def write(self, sync=1):
230 if not self.dirty: 231 return 232 233 self.merge() 234 235 db, mode = Get_DataBase(self.dir) 236 237 # Write using the path relative to the top of the SConstruct 238 # directory (self.dir.path), not relative to the top of 239 # the Repository; we only write to our own .sconsign file, 240 # not to .sconsign files in Repositories. 241 path = normcase(self.dir.path) 242 for key, entry in self.entries.items(): 243 entry.convert_to_sconsign() 244 db[path] = cPickle.dumps(self.entries, 1) 245 246 if sync: 247 try: 248 syncmethod = db.sync 249 except AttributeError: 250 # Not all anydbm modules have sync() methods. 251 pass 252 else: 253 syncmethod()
254
255 -class Dir(Base):
256 - def __init__(self, fp=None, dir=None):
257 """ 258 fp - file pointer to read entries from 259 """ 260 Base.__init__(self) 261 262 if not fp: 263 return 264 265 self.entries = cPickle.load(fp) 266 if type(self.entries) is not type({}): 267 self.entries = {} 268 raise TypeError 269 270 if dir: 271 for key, entry in self.entries.items(): 272 entry.convert_from_sconsign(dir, key)
273
274 -class DirFile(Dir):
275 """ 276 Encapsulates reading and writing a per-directory .sconsign file. 277 """
278 - def __init__(self, dir):
279 """ 280 dir - the directory for the file 281 """ 282 283 self.dir = dir 284 self.sconsign = os.path.join(dir.path, '.sconsign') 285 286 try: 287 fp = open(self.sconsign, 'rb') 288 except IOError: 289 fp = None 290 291 try: 292 Dir.__init__(self, fp, dir) 293 except KeyboardInterrupt: 294 raise 295 except: 296 SCons.Warnings.warn(SCons.Warnings