1#! /usr/bin/env python 2# 3# SCons - a Software Constructor 4# 5# Copyright (c) 2001 - 2014 The SCons Foundation 6# 7# Permission is hereby granted, free of charge, to any person obtaining 8# a copy of this software and associated documentation files (the 9# "Software"), to deal in the Software without restriction, including 10# without limitation the rights to use, copy, modify, merge, publish, 11# distribute, sublicense, and/or sell copies of the Software, and to 12# permit persons to whom the Software is furnished to do so, subject to 13# the following conditions: 14# 15# The above copyright notice and this permission notice shall be included 16# in all copies or substantial portions of the Software. 17# 18# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY 19# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE 20# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 22# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 24# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 26__revision__ = "src/script/sconsign.py 2014/08/24 12:12:31 garyo" 27 28__version__ = "2.3.3" 29 30__build__ = "" 31 32__buildsys__ = "lubuntu" 33 34__date__ = "2014/08/24 12:12:31" 35 36__developer__ = "garyo" 37 38import os 39import sys 40 41############################################################################## 42# BEGIN STANDARD SCons SCRIPT HEADER 43# 44# This is the cut-and-paste logic so that a self-contained script can 45# interoperate correctly with different SCons versions and installation 46# locations for the engine. If you modify anything in this section, you 47# should also change other scripts that use this same header. 48############################################################################## 49 50# Strip the script directory from sys.path() so on case-insensitive 51# (WIN32) systems Python doesn't think that the "scons" script is the 52# "SCons" package. Replace it with our own library directories 53# (version-specific first, in case they installed by hand there, 54# followed by generic) so we pick up the right version of the build 55# engine modules if they're in either directory. 56 57script_dir = sys.path[0] 58 59if script_dir in sys.path: 60 sys.path.remove(script_dir) 61 62libs = [] 63 64if "SCONS_LIB_DIR" in os.environ: 65 libs.append(os.environ["SCONS_LIB_DIR"]) 66 67local_version = 'scons-local-' + __version__ 68local = 'scons-local' 69if script_dir: 70 local_version = os.path.join(script_dir, local_version) 71 local = os.path.join(script_dir, local) 72libs.append(os.path.abspath(local_version)) 73libs.append(os.path.abspath(local)) 74 75scons_version = 'scons-%s' % __version__ 76 77# preferred order of scons lookup paths 78prefs = [] 79 80try: 81 import pkg_resources 82except ImportError: 83 pass 84else: 85 # when running from an egg add the egg's directory 86 try: 87 d = pkg_resources.get_distribution('scons') 88 except pkg_resources.DistributionNotFound: 89 pass 90 else: 91 prefs.append(d.location) 92 93if sys.platform == 'win32': 94 # sys.prefix is (likely) C:\Python*; 95 # check only C:\Python*. 96 prefs.append(sys.prefix) 97 prefs.append(os.path.join(sys.prefix, 'Lib', 'site-packages')) 98else: 99 # On other (POSIX) platforms, things are more complicated due to 100 # the variety of path names and library locations. Try to be smart 101 # about it. 102 if script_dir == 'bin': 103 # script_dir is `pwd`/bin; 104 # check `pwd`/lib/scons*. 105 prefs.append(os.getcwd()) 106 else: 107 if script_dir == '.' or script_dir == '': 108 script_dir = os.getcwd() 109 head, tail = os.path.split(script_dir) 110 if tail == "bin": 111 # script_dir is /foo/bin; 112 # check /foo/lib/scons*. 113 prefs.append(head) 114 115 head, tail = os.path.split(sys.prefix) 116 if tail == "usr": 117 # sys.prefix is /foo/usr; 118 # check /foo/usr/lib/scons* first, 119 # then /foo/usr/local/lib/scons*. 120 prefs.append(sys.prefix) 121 prefs.append(os.path.join(sys.prefix, "local")) 122 elif tail == "local": 123 h, t = os.path.split(head) 124 if t == "usr": 125 # sys.prefix is /foo/usr/local; 126 # check /foo/usr/local/lib/scons* first, 127 # then /foo/usr/lib/scons*. 128 prefs.append(sys.prefix) 129 prefs.append(head) 130 else: 131 # sys.prefix is /foo/local; 132 # check only /foo/local/lib/scons*. 133 prefs.append(sys.prefix) 134 else: 135 # sys.prefix is /foo (ends in neither /usr or /local); 136 # check only /foo/lib/scons*. 137 prefs.append(sys.prefix) 138 139 temp = [os.path.join(x, 'lib') for x in prefs] 140 temp.extend([os.path.join(x, 141 'lib', 142 'python' + sys.version[:3], 143 'site-packages') for x in prefs]) 144 prefs = temp 145 146 # Add the parent directory of the current python's library to the 147 # preferences. On SuSE-91/AMD64, for example, this is /usr/lib64, 148 # not /usr/lib. 149 try: 150 libpath = os.__file__ 151 except AttributeError: 152 pass 153 else: 154 # Split /usr/libfoo/python*/os.py to /usr/libfoo/python*. 155 libpath, tail = os.path.split(libpath) 156 # Split /usr/libfoo/python* to /usr/libfoo 157 libpath, tail = os.path.split(libpath) 158 # Check /usr/libfoo/scons*. 159 prefs.append(libpath) 160 161# Look first for 'scons-__version__' in all of our preference libs, 162# then for 'scons'. 163libs.extend([os.path.join(x, scons_version) for x in prefs]) 164libs.extend([os.path.join(x, 'scons') for x in prefs]) 165 166sys.path = libs + sys.path 167 168############################################################################## 169# END STANDARD SCons SCRIPT HEADER 170############################################################################## 171 172import SCons.compat # so pickle will import cPickle instead 173 174import whichdb 175import time 176import pickle 177import imp 178 179import SCons.SConsign 180 181def my_whichdb(filename): 182 if filename[-7:] == ".dblite": 183 return "SCons.dblite" 184 try: 185 f = open(filename + ".dblite", "rb") 186 f.close() 187 return "SCons.dblite" 188 except IOError: 189 pass 190 return _orig_whichdb(filename) 191 192_orig_whichdb = whichdb.whichdb 193whichdb.whichdb = my_whichdb 194 195def my_import(mname): 196 if '.' in mname: 197 i = mname.rfind('.') 198 parent = my_import(mname[:i]) 199 fp, pathname, description = imp.find_module(mname[i+1:], 200 parent.__path__) 201 else: 202 fp, pathname, description = imp.find_module(mname) 203 return imp.load_module(mname, fp, pathname, description) 204 205class Flagger(object): 206 default_value = 1 207 def __setitem__(self, item, value): 208 self.__dict__[item] = value 209 self.default_value = 0 210 def __getitem__(self, item): 211 return self.__dict__.get(item, self.default_value) 212 213Do_Call = None 214Print_Directories = [] 215Print_Entries = [] 216Print_Flags = Flagger() 217Verbose = 0 218Readable = 0 219 220def default_mapper(entry, name): 221 try: 222 val = eval("entry."+name) 223 except: 224 val = None 225 return str(val) 226 227def map_action(entry, name): 228 try: 229 bact = entry.bact 230 bactsig = entry.bactsig 231 except AttributeError: 232 return None 233 return '%s [%s]' % (bactsig, bact) 234 235def map_timestamp(entry, name): 236 try: 237 timestamp = entry.timestamp 238 except AttributeError: 239 timestamp = None 240 if Readable and timestamp: 241 return "'" + time.ctime(timestamp) + "'" 242 else: 243 return str(timestamp) 244 245def map_bkids(entry, name): 246 try: 247 bkids = entry.bsources + entry.bdepends + entry.bimplicit 248 bkidsigs = entry.bsourcesigs + entry.bdependsigs + entry.bimplicitsigs 249 except AttributeError: 250 return None 251 result = [] 252 for i in range(len(bkids)): 253 result.append(nodeinfo_string(bkids[i], bkidsigs[i], " ")) 254 if result == []: 255 return None 256 return "\n ".join(result) 257 258map_field = { 259 'action' : map_action, 260 'timestamp' : map_timestamp, 261 'bkids' : map_bkids, 262} 263 264map_name = { 265 'implicit' : 'bkids', 266} 267 268def field(name, entry, verbose=Verbose): 269 if not Print_Flags[name]: 270 return None 271 fieldname = map_name.get(name, name) 272 mapper = map_field.get(fieldname, default_mapper) 273 val = mapper(entry, name) 274 if verbose: 275 val = name + ": " + val 276 return val 277 278def nodeinfo_raw(name, ninfo, prefix=""): 279 # This just formats the dictionary, which we would normally use str() 280 # to do, except that we want the keys sorted for deterministic output. 281 d = ninfo.__dict__ 282 try: 283 keys = ninfo.field_list + ['_version_id'] 284 except AttributeError: 285 keys = sorted(d.keys()) 286 l = [] 287 for k in keys: 288 l.append('%s: %s' % (repr(k), repr(d.get(k)))) 289 if '\n' in name: 290 name = repr(name) 291 return name + ': {' + ', '.join(l) + '}' 292 293def nodeinfo_cooked(name, ninfo, prefix=""): 294 try: 295 field_list = ninfo.field_list 296 except AttributeError: 297 field_list = [] 298 if '\n' in name: 299 name = repr(name) 300 outlist = [name+':'] + [_f for _f in [field(x, ninfo, Verbose) for x in field_list] if _f] 301 if Verbose: 302 sep = '\n ' + prefix 303 else: 304 sep = ' ' 305 return sep.join(outlist) 306 307nodeinfo_string = nodeinfo_cooked 308 309def printfield(name, entry, prefix=""): 310 outlist = field("implicit", entry, 0) 311 if outlist: 312 if Verbose: 313 print " implicit:" 314 print " " + outlist 315 outact = field("action", entry, 0) 316 if outact: 317 if Verbose: 318 print " action: " + outact 319 else: 320 print " " + outact 321 322def printentries(entries, location): 323 if Print_Entries: 324 for name in Print_Entries: 325 try: 326 entry = entries[name] 327 except KeyError: 328 sys.stderr.write("sconsign: no entry `%s' in `%s'\n" % (name, location)) 329 else: 330 try: 331 ninfo = entry.ninfo 332 except AttributeError: 333 print name + ":" 334 else: 335 print nodeinfo_string(name, entry.ninfo) 336 printfield(name, entry.binfo) 337 else: 338 for name in sorted(entries.keys()): 339 entry = entries[name] 340 try: 341 ninfo = entry.ninfo 342 except AttributeError: 343 print name + ":" 344 else: 345 print nodeinfo_string(name, entry.ninfo) 346 printfield(name, entry.binfo) 347 348class Do_SConsignDB(object): 349 def __init__(self, dbm_name, dbm): 350 self.dbm_name = dbm_name 351 self.dbm = dbm 352 353 def __call__(self, fname): 354 # The *dbm modules stick their own file suffixes on the names 355 # that are passed in. This is causes us to jump through some 356 # hoops here to be able to allow the user 357 try: 358 # Try opening the specified file name. Example: 359 # SPECIFIED OPENED BY self.dbm.open() 360 # --------- ------------------------- 361 # .sconsign => .sconsign.dblite 362 # .sconsign.dblite => .sconsign.dblite.dblite 363 db = self.dbm.open(fname, "r") 364 except (IOError, OSError), e: 365 print_e = e 366 try: 367 # That didn't work, so try opening the base name, 368 # so that if the actually passed in 'sconsign.dblite' 369 # (for example), the dbm module will put the suffix back 370 # on for us and open it anyway. 371 db = self.dbm.open(os.path.splitext(fname)[0], "r") 372 except (IOError, OSError): 373 # That didn't work either. See if the file name 374 # they specified just exists (independent of the dbm 375 # suffix-mangling). 376 try: 377 open(fname, "r") 378 except (IOError, OSError), e: 379 # Nope, that file doesn't even exist, so report that 380 # fact back. 381 print_e = e 382 sys.stderr.write("sconsign: %s\n" % (print_e)) 383 return 384 except KeyboardInterrupt: 385 raise 386 except pickle.UnpicklingError: 387 sys.stderr.write("sconsign: ignoring invalid `%s' file `%s'\n" % (self.dbm_name, fname)) 388 return 389 except Exception, e: 390 sys.stderr.write("sconsign: ignoring invalid `%s' file `%s': %s\n" % (self.dbm_name, fname, e)) 391 return 392 393 if Print_Directories: 394 for dir in Print_Directories: 395 try: 396 val = db[dir] 397 except KeyError: 398 sys.stderr.write("sconsign: no dir `%s' in `%s'\n" % (dir, args[0])) 399 else: 400 self.printentries(dir, val) 401 else: 402 for dir in sorted(db.keys()): 403 self.printentries(dir, db[dir]) 404 405 def printentries(self, dir, val): 406 print '=== ' + dir + ':' 407 printentries(pickle.loads(val), dir) 408 409def Do_SConsignDir(name): 410 try: 411 fp = open(name, 'rb') 412 except (IOError, OSError), e: 413 sys.stderr.write("sconsign: %s\n" % (e)) 414 return 415 try: 416 sconsign = SCons.SConsign.Dir(fp) 417 except KeyboardInterrupt: 418 raise 419 except pickle.UnpicklingError: 420 sys.stderr.write("sconsign: ignoring invalid .sconsign file `%s'\n" % (name)) 421 return 422 except Exception, e: 423 sys.stderr.write("sconsign: ignoring invalid .sconsign file `%s': %s\n" % (name, e)) 424 return 425 printentries(sconsign.entries, args[0]) 426 427############################################################################## 428 429import getopt 430 431helpstr = """\ 432Usage: sconsign [OPTIONS] FILE [...] 433Options: 434 -a, --act, --action Print build action information. 435 -c, --csig Print content signature information. 436 -d DIR, --dir=DIR Print only info about DIR. 437 -e ENTRY, --entry=ENTRY Print only info about ENTRY. 438 -f FORMAT, --format=FORMAT FILE is in the specified FORMAT. 439 -h, --help Print this message and exit. 440 -i, --implicit Print implicit dependency information. 441 -r, --readable Print timestamps in human-readable form. 442 --raw Print raw Python object representations. 443 -s, --size Print file sizes. 444 -t, --timestamp Print timestamp information. 445 -v, --verbose Verbose, describe each field. 446""" 447 448opts, args = getopt.getopt(sys.argv[1:], "acd:e:f:hirstv", 449 ['act', 'action', 450 'csig', 'dir=', 'entry=', 451 'format=', 'help', 'implicit', 452 'raw', 'readable', 453 'size', 'timestamp', 'verbose']) 454 455 456for o, a in opts: 457 if o in ('-a', '--act', '--action'): 458 Print_Flags['action'] = 1 459 elif o in ('-c', '--csig'): 460 Print_Flags['csig'] = 1 461 elif o in ('-d', '--dir'): 462 Print_Directories.append(a) 463 elif o in ('-e', '--entry'): 464 Print_Entries.append(a) 465 elif o in ('-f', '--format'): 466 Module_Map = {'dblite' : 'SCons.dblite', 467 'sconsign' : None} 468 dbm_name = Module_Map.get(a, a) 469 if dbm_name: 470 try: 471 dbm = my_import(dbm_name) 472 except: 473 sys.stderr.write("sconsign: illegal file format `%s'\n" % a) 474 print helpstr 475 sys.exit(2) 476 Do_Call = Do_SConsignDB(a, dbm) 477 else: 478 Do_Call = Do_SConsignDir 479 elif o in ('-h', '--help'): 480 print helpstr 481 sys.exit(0) 482 elif o in ('-i', '--implicit'): 483 Print_Flags['implicit'] = 1 484 elif o in ('--raw',): 485 nodeinfo_string = nodeinfo_raw 486 elif o in ('-r', '--readable'): 487 Readable = 1 488 elif o in ('-s', '--size'): 489 Print_Flags['size'] = 1 490 elif o in ('-t', '--timestamp'): 491 Print_Flags['timestamp'] = 1 492 elif o in ('-v', '--verbose'): 493 Verbose = 1 494 495if Do_Call: 496 for a in args: 497 Do_Call(a) 498else: 499 for a in args: 500 dbm_name = whichdb.whichdb(a) 501 if dbm_name: 502 Map_Module = {'SCons.dblite' : 'dblite'} 503 dbm = my_import(dbm_name) 504 Do_SConsignDB(Map_Module.get(dbm_name, dbm_name), dbm)(a) 505 else: 506 Do_SConsignDir(a) 507 508sys.exit(0) 509 510# Local Variables: 511# tab-width:4 512# indent-tabs-mode:nil 513# End: 514# vim: set expandtab tabstop=4 shiftwidth=4: 515