http://scons.tigris.org/issues/show_bug.cgi?id=2329
[scons.git] / src / script / sconsign.py
index a886fee6629e61d955d4ad32582dab5ccea1f04c..ac619a58b4694fd5bf0e6de2c8ced18991f6a16b 100644 (file)
@@ -23,6 +23,7 @@
 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 #
+from __future__ import generators  ### KEEP FOR COMPATIBILITY FIXERS
 
 __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
 
@@ -64,13 +65,16 @@ if script_dir in sys.path:
 
 libs = []
 
-if os.environ.has_key("SCONS_LIB_DIR"):
+if "SCONS_LIB_DIR" in os.environ:
     libs.append(os.environ["SCONS_LIB_DIR"])
 
-local = 'scons-local-' + __version__
+local_version = 'scons-local-' + __version__
+local = 'scons-local'
 if script_dir:
+    local_version = os.path.join(script_dir, local_version)
     local = os.path.join(script_dir, local)
-libs.append(local)
+libs.append(os.path.abspath(local_version))
+libs.append(os.path.abspath(local))
 
 scons_version = 'scons-%s' % __version__
 
@@ -122,18 +126,45 @@ else:
         # check only /foo/lib/scons*.
         prefs.append(sys.prefix)
 
-    temp = map(lambda x: os.path.join(x, 'lib'), prefs)
-    temp.extend(map(lambda x: os.path.join(x,
+    temp = [os.path.join(x, 'lib') for x in prefs]
+    temp.extend([os.path.join(x,
                                            'lib',
                                            'python' + sys.version[:3],
-                                           'site-packages'),
-                           prefs))
+                                           'site-packages') for x in prefs])
     prefs = temp
 
+    # Add the parent directory of the current python's library to the
+    # preferences.  On SuSE-91/AMD64, for example, this is /usr/lib64,
+    # not /usr/lib.
+    try:
+        libpath = os.__file__
+    except AttributeError:
+        pass
+    else:
+        # Split /usr/libfoo/python*/os.py to /usr/libfoo/python*.
+        libpath, tail = os.path.split(libpath)
+        # Split /usr/libfoo/python* to /usr/libfoo
+        libpath, tail = os.path.split(libpath)
+        # Check /usr/libfoo/scons*.
+        prefs.append(libpath)
+
+    try:
+        import pkg_resources
+    except ImportError:
+        pass
+    else:
+        # when running from an egg add the egg's directory 
+        try:
+            d = pkg_resources.get_distribution('scons')
+        except pkg_resources.DistributionNotFound:
+            pass
+        else:
+            prefs.append(d.location)
+
 # Look first for 'scons-__version__' in all of our preference libs,
 # then for 'scons'.
-libs.extend(map(lambda x: os.path.join(x, scons_version), prefs))
-libs.extend(map(lambda x: os.path.join(x, 'scons'), prefs))
+libs.extend([os.path.join(x, scons_version) for x in prefs])
+libs.extend([os.path.join(x, 'scons') for x in prefs])
 
 sys.path = libs + sys.path
 
@@ -143,12 +174,13 @@ sys.path = libs + sys.path
 
 import cPickle
 import imp
-import string
 import whichdb
 
 import SCons.SConsign
 
 def my_whichdb(filename):
+    if filename[-7:] == ".dblite":
+        return "SCons.dblite"
     try:
         f = open(filename + ".dblite", "rb")
         f.close()
@@ -162,7 +194,7 @@ whichdb.whichdb = my_whichdb
 
 def my_import(mname):
     if '.' in mname:
-        i = string.rfind(mname, '.')
+        i = mname.rfind('.')
         parent = my_import(mname[:i])
         fp, pathname, description = imp.find_module(mname[i+1:],
                                                     parent.__path__)
@@ -192,6 +224,14 @@ def default_mapper(entry, name):
         val = None
     return str(val)
 
+def map_action(entry, name):
+    try:
+        bact = entry.bact
+        bactsig = entry.bactsig
+    except AttributeError:
+        return None
+    return '%s [%s]' % (bactsig, bact)
+
 def map_timestamp(entry, name):
     try:
         timestamp = entry.timestamp
@@ -210,12 +250,13 @@ def map_bkids(entry, name):
         return None
     result = []
     for i in xrange(len(bkids)):
-        result.append("%s: %s" % (bkids[i], bkidsigs[i]))
+        result.append(nodeinfo_string(bkids[i], bkidsigs[i], "        "))
     if result == []:
         return None
-    return string.join(result, "\n        ")
+    return "\n        ".join(result)
 
 map_field = {
+    'action'    : map_action,
     'timestamp' : map_timestamp,
     'bkids'     : map_bkids,
 }
@@ -224,40 +265,85 @@ map_name = {
     'implicit'  : 'bkids',
 }
 
-def printfield(name, entry):
-    def field(name, verbose=Verbose, entry=entry):
-        if not Print_Flags[name]:
-            return None
-        fieldname = map_name.get(name, name)
-        mapper = map_field.get(fieldname, default_mapper)
-        val = mapper(entry, name)
-        if verbose:
-            val = name + ": " + val
-        return val
-
-    fieldlist = ["timestamp", "bsig", "csig"]
-    outlist = [name+":"] + filter(None, map(field, fieldlist))
-    sep = Verbose and "\n    " or " "
-    print string.join(outlist, sep)
-
-    outlist = field("implicit", 0)
+def field(name, entry, verbose=Verbose):
+    if not Print_Flags[name]:
+        return None
+    fieldname = map_name.get(name, name)
+    mapper = map_field.get(fieldname, default_mapper)
+    val = mapper(entry, name)
+    if verbose:
+        val = name + ": " + val
+    return val
+
+def nodeinfo_raw(name, ninfo, prefix=""):
+    # This just formats the dictionary, which we would normally use str()
+    # to do, except that we want the keys sorted for deterministic output.
+    d = ninfo.__dict__
+    try:
+        keys = ninfo.field_list + ['_version_id']
+    except AttributeError:
+        keys = sorted(d.keys())
+    l = []
+    for k in keys:
+        l.append('%s: %s' % (repr(k), repr(d.get(k))))
+    if '\n' in name:
+        name = repr(name)
+    return name + ': {' + ', '.join(l) + '}'
+
+def nodeinfo_cooked(name, ninfo, prefix=""):
+    try:
+        field_list = ninfo.field_list
+    except AttributeError:
+        field_list = []
+    if '\n' in name:
+        name = repr(name)
+    outlist = [name+':'] + [_f for _f in [field(x, ninfo, Verbose) for x in field_list] if _f]
+    if Verbose:
+        sep = '\n    ' + prefix
+    else:
+        sep = ' '
+    return sep.join(outlist)
+
+nodeinfo_string = nodeinfo_cooked
+
+def printfield(name, entry, prefix=""):
+    outlist = field("implicit", entry, 0)
     if outlist:
         if Verbose:
             print "    implicit:"
         print "        " + outlist
+    outact = field("action", entry, 0)
+    if outact:
+        if Verbose:
+            print "    action: " + outact
+        else:
+            print "        " + outact
 
-def printentries(entries):
+def printentries(entries, location):
     if Print_Entries:
         for name in Print_Entries:
             try:
                 entry = entries[name]
             except KeyError:
-                sys.stderr.write("sconsign: no entry `%s' in `%s'\n" % (name, args[0]))
+                sys.stderr.write("sconsign: no entry `%s' in `%s'\n" % (name, location))
             else:
-                printfield(name, entry)
+                try:
+                    ninfo = entry.ninfo
+                except AttributeError:
+                    print name + ":"
+                else:
+                    print nodeinfo_string(name, entry.ninfo)
+                printfield(name, entry.binfo)
     else:
-        for name, e in entries.items():
-            printfield(name, e)
+        for name in sorted(entries.keys()):
+            entry = entries[name]
+            try:
+                ninfo = entry.ninfo
+            except AttributeError:
+                print name + ":"
+            else:
+                print nodeinfo_string(name, entry.ninfo)
+            printfield(name, entry.binfo)
 
 class Do_SConsignDB:
     def __init__(self, dbm_name, dbm):
@@ -295,9 +381,14 @@ class Do_SConsignDB:
                     print_e = e
                 sys.stderr.write("sconsign: %s\n" % (print_e))
                 return
-        except:
+        except KeyboardInterrupt:
+            raise
+        except cPickle.UnpicklingError:
             sys.stderr.write("sconsign: ignoring invalid `%s' file `%s'\n" % (self.dbm_name, fname))
             return
+        except Exception, e:
+            sys.stderr.write("sconsign: ignoring invalid `%s' file `%s': %s\n" % (self.dbm_name, fname, e))
+            return
 
         if Print_Directories:
             for dir in Print_Directories:
@@ -308,14 +399,12 @@ class Do_SConsignDB:
                 else:
                     self.printentries(dir, val)
         else:
-            keys = db.keys()
-            keys.sort()
-            for dir in keys:
+            for dir in sorted(db.keys()):
                 self.printentries(dir, db[dir])
 
     def printentries(self, dir, val):
         print '=== ' + dir + ':'
-        printentries(cPickle.loads(val))
+        printentries(cPickle.loads(val), dir)
 
 def Do_SConsignDir(name):
     try:
@@ -325,10 +414,15 @@ def Do_SConsignDir(name):
         return
     try:
         sconsign = SCons.SConsign.Dir(fp)
-    except:
-        sys.stderr.write("sconsign: ignoring invalid .sconsign file `%s'\n" % name)
+    except KeyboardInterrupt:
+        raise
+    except cPickle.UnpicklingError:
+        sys.stderr.write("sconsign: ignoring invalid .sconsign file `%s'\n" % (name))
         return
-    printentries(sconsign.entries)
+    except Exception, e:
+        sys.stderr.write("sconsign: ignoring invalid .sconsign file `%s': %s\n" % (name, e))
+        return
+    printentries(sconsign.entries, args[0])
 
 ##############################################################################
 
@@ -337,7 +431,7 @@ import getopt
 helpstr = """\
 Usage: sconsign [OPTIONS] FILE [...]
 Options:
-  -b, --bsig                  Print build signature information.
+  -a, --act, --action         Print build action information.
   -c, --csig                  Print content signature information.
   -d DIR, --dir=DIR           Print only info about DIR.
   -e ENTRY, --entry=ENTRY     Print only info about ENTRY.
@@ -345,19 +439,23 @@ Options:
   -h, --help                  Print this message and exit.
   -i, --implicit              Print implicit dependency information.
   -r, --readable              Print timestamps in human-readable form.
+  --raw                       Print raw Python object representations.
+  -s, --size                  Print file sizes.
   -t, --timestamp             Print timestamp information.
   -v, --verbose               Verbose, describe each field.
 """
 
-opts, args = getopt.getopt(sys.argv[1:], "bcd:e:f:hirtv",
-                            ['bsig', 'csig', 'dir=', 'entry=',
+opts, args = getopt.getopt(sys.argv[1:], "acd:e:f:hirstv",
+                            ['act', 'action',
+                             'csig', 'dir=', 'entry=',
                              'format=', 'help', 'implicit',
-                             'readable', 'timestamp', 'verbose'])
+                             'raw', 'readable',
+                             'size', 'timestamp', 'verbose'])
 
 
 for o, a in opts:
-    if o in ('-b', '--bsig'):
-        Print_Flags['bsig'] = 1
+    if o in ('-a', '--act', '--action'):
+        Print_Flags['action'] = 1
     elif o in ('-c', '--csig'):
         Print_Flags['csig'] = 1
     elif o in ('-d', '--dir'):
@@ -383,8 +481,12 @@ for o, a in opts:
         sys.exit(0)
     elif o in ('-i', '--implicit'):
         Print_Flags['implicit'] = 1
+    elif o in ('--raw',):
+        nodeinfo_string = nodeinfo_raw
     elif o in ('-r', '--readable'):
         Readable = 1
+    elif o in ('-s', '--size'):
+        Print_Flags['size'] = 1
     elif o in ('-t', '--timestamp'):
         Print_Flags['timestamp'] = 1
     elif o in ('-v', '--verbose'):
@@ -404,3 +506,9 @@ else:
             Do_SConsignDir(a)
 
 sys.exit(0)
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4: