blob: f6f3fad20a031fc1644ec593987d0fd32d35f333 [file] [log] [blame]
# Copyright 2016 Google Inc. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
def dump(obj, obj_name):
"""Debugging method that recursively prints object fields to stderr
Args:
obj: Object to dump
obj_name: Name to print for that object
Example Usage:
```
load("debug", "dump")
...
dump(ctx, "ctx")
```
Example Output:
```
WARNING: /code/rrrrr/subpar/debug.bzl:11:5:
ctx[ctx]:
action[string]: <getattr(action) failed>
attr[struct]:
_action_listener[list]: []
_compiler[RuleConfiguredTarget]:
data_runfiles[runfiles]:
```
"""
s = '\n' + _dumpstr(obj, obj_name)
print(s)
def _dumpstr(root_obj, root_obj_name):
"""Helper method for dump() to just generate the string
Some fields always raise errors if we getattr() on them. We
manually blacklist them here. Other fields raise errors only if
we getattr() without a default. Those are handled below.
A bug was filed against Bazel, but it got fixed in a way that
didn't actually fix this.
"""
BLACKLIST = [
"InputFileConfiguredTarget.output_group",
"Label.Label",
"Label.relative",
"License.to_json",
"RuleConfiguredTarget.output_group",
"ctx.action",
"ctx.check_placeholders",
"ctx.empty_action",
"ctx.expand",
"ctx.expand_location",
"ctx.expand_make_variables",
"ctx.file_action",
"ctx.middle_man",
"ctx.new_file",
"ctx.resolve_command",
"ctx.rule",
"ctx.runfiles",
"ctx.template_action",
"ctx.tokenize",
"fragments.apple",
"fragments.cpp",
"fragments.java",
"fragments.jvm",
"fragments.objc",
"runfiles.symlinks",
"struct.output_licenses",
"struct.to_json",
"struct.to_proto",
]
MAXLINES = 4000
ROOT_MAXDEPTH = 5
# List of printable lines
lines = []
# Bazel doesn't allow a function to recursively call itself, so
# use an explicit stack
stack = [(root_obj, root_obj_name, 0, ROOT_MAXDEPTH)]
# No while() in Bazel, so use for loop over large range
for _ in range(MAXLINES):
if len(stack) == 0:
break
obj, obj_name, indent, maxdepth = stack.pop()
obj_type = type(obj)
indent_str = ' '*indent
line = '{indent_str}{obj_name}[{obj_type}]:'.format(
indent_str=indent_str, obj_name=obj_name, obj_type=obj_type)
if maxdepth == 0 or obj_type in ['dict', 'list', 'set', 'string']:
# Dump value as string, inline
line += ' ' + str(obj)
else:
# Dump all of value's fields on separate lines
attrs = dir(obj)
# Push each field to stack in reverse order, so they pop
# in sorted order
for attr in reversed(attrs):
if "%s.%s" % (obj_type, attr) in BLACKLIST:
value = '<blacklisted attr (%s)>' % attr
else:
value = getattr(obj, attr, '<getattr(%s) failed>' % attr)
stack.append((value, attr, indent+4, maxdepth-1))
lines.append(line)
return '\n'.join(lines)