mapnik/scons/scons-local-4.5.2/SCons/Utilities/sconsign.py
2023-09-22 14:38:30 +01:00

514 lines
15 KiB
Python
Vendored

#! /usr/bin/env python
#
# SCons - a Software Constructor
#
# MIT License
#
# Copyright 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.
"""Utility script to dump information from SCons signature database."""
import getopt
import importlib
import os
import sys
from dbm import whichdb
import time
import pickle
import SCons.compat
import SCons.SConsign
def my_whichdb(filename):
if filename[-7:] == ".dblite":
return "SCons.dblite"
try:
with open(filename + ".dblite", "rb"):
return "SCons.dblite"
except IOError:
pass
return whichdb(filename)
def my_import(mname):
"""Import database module.
This was used if the module was *not* SCons.dblite, to allow
for programmatic importing. It is no longer used, in favor of
importlib.import_module, and will be removed eventually.
"""
import imp
if '.' in mname:
i = mname.rfind('.')
parent = my_import(mname[:i])
fp, pathname, description = imp.find_module(mname[i+1:], parent.__path__)
else:
fp, pathname, description = imp.find_module(mname)
return imp.load_module(mname, fp, pathname, description)
class Flagger:
default_value = 1
def __setitem__(self, item, value):
self.__dict__[item] = value
self.default_value = 0
def __getitem__(self, item):
return self.__dict__.get(item, self.default_value)
Do_Call = None
Print_Directories = []
Print_Entries = []
Print_Flags = Flagger()
Verbose = 0
Readable = 0
Warns = 0
def default_mapper(entry, name):
"""
Stringify an entry that doesn't have an explicit mapping.
Args:
entry: entry
name: field name
Returns: str
"""
try:
val = eval("entry." + name)
except AttributeError:
val = None
return str(val)
def map_action(entry, _):
"""
Stringify an action entry and signature.
Args:
entry: action entry
second argument is not used
Returns: str
"""
try:
bact = entry.bact
bactsig = entry.bactsig
except AttributeError:
return None
return '%s [%s]' % (bactsig, bact)
def map_timestamp(entry, _):
"""
Stringify a timestamp entry.
Args:
entry: timestamp entry
second argument is not used
Returns: str
"""
try:
timestamp = entry.timestamp
except AttributeError:
timestamp = None
if Readable and timestamp:
return "'" + time.ctime(timestamp) + "'"
else:
return str(timestamp)
def map_bkids(entry, _):
"""
Stringify an implicit entry.
Args:
entry:
second argument is not used
Returns: str
"""
try:
bkids = entry.bsources + entry.bdepends + entry.bimplicit
bkidsigs = entry.bsourcesigs + entry.bdependsigs + entry.bimplicitsigs
except AttributeError:
return None
if len(bkids) != len(bkidsigs):
global Warns
Warns += 1
# add warning to result rather than direct print so it will line up
msg = "Warning: missing information, {} ids but {} sigs"
result = [msg.format(len(bkids), len(bkidsigs))]
else:
result = []
result += [nodeinfo_string(bkid, bkidsig, " ")
for bkid, bkidsig in zip(bkids, bkidsigs)]
if not result:
return None
return "\n ".join(result)
map_field = {
'action' : map_action,
'timestamp' : map_timestamp,
'bkids' : map_bkids,
}
map_name = {
'implicit' : 'bkids',
}
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.__getstate__()
try:
keys = ninfo.field_list + ['_version_id']
except AttributeError:
keys = sorted(d.keys())
values = []
for key in keys:
values.append('%s: %s' % (repr(key), repr(d.get(key))))
if '\n' in name:
name = repr(name)
return name + ': {' + ', '.join(values) + '}'
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, location):
if Print_Entries:
for name in Print_Entries:
try:
entry = entries[name]
except KeyError:
err = "sconsign: no entry `%s' in `%s'\n" % (name, location)
sys.stderr.write(err)
else:
try:
ninfo = entry.ninfo
except AttributeError:
print(name + ":")
else:
print(nodeinfo_string(name, entry.ninfo))
printfield(name, entry.binfo)
else:
for name in sorted(entries.keys()):
entry = entries[name]
try:
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):
self.dbm_name = dbm_name
self.dbm = dbm
def __call__(self, fname):
# The *dbm modules stick their own file suffixes on the names
# that are passed in. This causes us to jump through some
# hoops here.
try:
# Try opening the specified file name. Example:
# SPECIFIED OPENED BY self.dbm.open()
# --------- -------------------------
# .sconsign => .sconsign.dblite
# .sconsign.dblite => .sconsign.dblite.dblite
db = self.dbm.open(fname, "r")
except (IOError, OSError) as e:
print_e = e
try:
# That didn't work, so try opening the base name,
# so that if they actually passed in 'sconsign.dblite'
# (for example), the dbm module will put the suffix back
# on for us and open it anyway.
db = self.dbm.open(os.path.splitext(fname)[0], "r")
except (IOError, OSError):
# That didn't work either. See if the file name
# they specified even exists (independent of the dbm
# suffix-mangling).
try:
with open(fname, "rb"):
pass # this is a touch only, we don't use it here.
except (IOError, OSError) as e:
# Nope, that file doesn't even exist, so report that
# fact back.
print_e = e
sys.stderr.write("sconsign: %s\n" % print_e)
return
except KeyboardInterrupt:
raise
except pickle.UnpicklingError:
sys.stderr.write("sconsign: ignoring invalid `%s' file `%s'\n"
% (self.dbm_name, fname))
return
except Exception as e:
sys.stderr.write("sconsign: ignoring invalid `%s' file `%s': %s\n"
% (self.dbm_name, fname, e))
exc_type, _, _ = sys.exc_info()
if exc_type.__name__ == "ValueError":
sys.stderr.write("unrecognized pickle protocol.\n")
return
if Print_Directories:
for dir in Print_Directories:
try:
val = db[dir]
except KeyError:
err = "sconsign: no dir `%s' in `%s'\n" % (dir, args[0])
sys.stderr.write(err)
else:
self.printentries(dir, val)
else:
for dir in sorted(db.keys()):
self.printentries(dir, db[dir])
@staticmethod
def printentries(dir, val):
try:
print('=== ' + dir + ':')
except TypeError:
print('=== ' + dir.decode() + ':')
printentries(pickle.loads(val), dir)
def Do_SConsignDir(name):
try:
with open(name, 'rb') as fp:
try:
sconsign = SCons.SConsign.Dir(fp)
except KeyboardInterrupt:
raise
except pickle.UnpicklingError:
err = "sconsign: ignoring invalid .sconsign file `%s'\n" % name
sys.stderr.write(err)
return
except Exception as e:
err = "sconsign: ignoring invalid .sconsign file `%s': %s\n" % (name, e)
sys.stderr.write(err)
return
printentries(sconsign.entries, args[0])
except (IOError, OSError) as e:
sys.stderr.write("sconsign: %s\n" % e)
return
##############################################################################
def main():
global Do_Call
global nodeinfo_string
global args
global Verbose
global Readable
helpstr = """\
Usage: sconsign [OPTIONS] [FILE ...]
Options:
-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.
-f FORMAT, --format=FORMAT FILE is in the specified FORMAT.
-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.
"""
try:
opts, args = getopt.getopt(
sys.argv[1:],
'acd:e:f:hirstv',
[
'act',
'action',
'csig',
'dir=',
'entry=',
'format=',
'help',
'implicit',
'raw',
'readable',
'size',
'timestamp',
'verbose',
],
)
except getopt.GetoptError as err:
sys.stderr.write(str(err) + '\n')
print(helpstr)
sys.exit(2)
for o, a in opts:
if o in ('-a', '--act', '--action'):
Print_Flags['action'] = 1
elif o in ('-c', '--csig'):
Print_Flags['csig'] = 1
elif o in ('-d', '--dir'):
Print_Directories.append(a)
elif o in ('-e', '--entry'):
Print_Entries.append(a)
elif o in ('-f', '--format'):
# Try to map the given DB format to a known module
# name, that we can then try to import...
Module_Map = {'dblite': 'SCons.dblite', 'sconsign': None}
dbm_name = Module_Map.get(a, a)
if dbm_name:
try:
if dbm_name != "SCons.dblite":
dbm = importlib.import_module(dbm_name)
else:
import SCons.dblite
dbm = SCons.dblite
# Ensure that we don't ignore corrupt DB files,
# this was handled by calling my_import('SCons.dblite')
# again in earlier versions...
SCons.dblite.IGNORE_CORRUPT_DBFILES = False
except ImportError:
sys.stderr.write("sconsign: illegal file format `%s'\n" % a)
print(helpstr)
sys.exit(2)
Do_Call = Do_SConsignDB(a, dbm)
else:
Do_Call = Do_SConsignDir
elif o in ('-h', '--help'):
print(helpstr)
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'):
Verbose = 1
if Do_Call:
for a in args:
Do_Call(a)
else:
if not args:
args = [".sconsign.dblite"]
for a in args:
dbm_name = my_whichdb(a)
if dbm_name:
Map_Module = {'SCons.dblite': 'dblite'}
if dbm_name != "SCons.dblite":
dbm = importlib.import_module(dbm_name)
else:
import SCons.dblite
dbm = SCons.dblite
# Ensure that we don't ignore corrupt DB files,
# this was handled by calling my_import('SCons.dblite')
# again in earlier versions...
SCons.dblite.IGNORE_CORRUPT_DBFILES = False
Do_SConsignDB(Map_Module.get(dbm_name, dbm_name), dbm)(a)
else:
Do_SConsignDir(a)
if Warns:
print("NOTE: there were %d warnings, please check output" % Warns)
if __name__ == "__main__":
main()
sys.exit(0)
# Local Variables:
# tab-width:4
# indent-tabs-mode:nil
# End:
# vim: set expandtab tabstop=4 shiftwidth=4: