blob: 6e069ff459da11e97de298891e528c05a8838fdf [file] [log] [blame]
#!/usr/bin/env fuchsia-vendored-python
# Copyright 2023 The Fuchsia Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
#
# IMPORTANT: This script should not depend on any Bazel workspace setup
# or specific environment. Only depend on standard Python3 modules
# and assume a minimum of Python3.8 is being used.
"""A tool to perform useful Bazel operations easily."""
import argparse
import os
import re
import subprocess
import sys
from pathlib import Path
from typing import Sequence
def make_bazel_quiet_command(bazel: str, command: str) -> Sequence[str]:
"""Create command argument list for a Bazel command that does not print too much.
Args:
bazel: Path to Bazel program.
command: Bazel command (e.g. 'query', 'build', etc..)
Returns:
A sequence of strings that can be used as a command line prefix.
"""
result = [
bazel,
command,
"--noshow_loading_progress",
"--noshow_progress",
"--ui_event_filters=-info",
]
if command != 'query':
result += [ "--show_result=0" ]
return result
def cmd_target_dump(args: argparse.Namespace) -> int:
"""Implement the target_dump command."""
bazel_cmd = make_bazel_quiet_command(args.bazel, 'query') + [ '--output=build' ] + args.target_set
buildifier_cmd = [args.buildifier, '--type=build', '--mode=fix', '--lint=off']
proc1 = subprocess.Popen(bazel_cmd, stdin=subprocess.DEVNULL, stdout=subprocess.PIPE, stderr=None)
proc2 = subprocess.Popen(buildifier_cmd, stdin=proc1.stdout, stdout=None, stderr=None)
proc1.wait()
proc2.wait()
if proc1.returncode != 0:
return proc1.returncode
return proc2.returncode
def cmd_actions(args: argparse.Namespace) -> int:
"""Implement the 'actions' command."""
bazel_cmd = make_bazel_quiet_command(args.bazel, 'aquery') + args.extra_args[1:] + args.target_set
print('CMD %s' % bazel_cmd)
ret = subprocess.run(bazel_cmd, stdin=subprocess.DEVNULL, capture_output=True, text=True)
if ret.returncode != 0:
print('%s\n' % ret.stdout, file=sys.stdout)
print('%s\n' % ret.stderr, file=sys.stderr)
return ret.returncode
def ignored_path(path):
if path.startswith(('external/prebuilt_clang/',
'external/fuchsia_clang/',
'prebuilt/third_party/sysroot/',)):
return True
if path.find('external/fuchsia_prebuilt_rust/') >= 0:
return True
return False
inputs_re = re.compile('^ Inputs: \[(.*)\]$')
for line in ret.stdout.splitlines():
m = inputs_re.match(line)
if m:
input_paths = []
for path in m.group(1).split(','):
path = path.strip()
if not ignored_path(path):
input_paths.append(path)
line = ' Inputs: [%s]' % (', '.join(input_paths))
print(line)
return 0
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--bazel', default = 'bazel', help='Specify bazel executable')
parser.add_argument('--buildifier', default = 'buildifier', help='Specify buildifier executable')
parser.add_argument('--workspace', help='Specify workspace directory')
subparsers = parser.add_subparsers(help='available commands')
# The target_dump command.
parser_target_dump = subparsers.add_parser(
'target_dump',
help='Dump definitions of Bazel targets.',
formatter_class=argparse.RawDescriptionHelpFormatter,
description=r'''
Print the real definition of a target, or set of targets, in a
Bazel graph, after all macros have been expanded. Note that:
- Each TARGET_SET argument is a standard Bazel target set expression
(e.g. `//src/lib:foo` or `//:*`).
- The output is pretty printed for readability.
- For each target that is the result of a macro expansion, comments
are added to descirbe the call chain that led to it.
- For each target generated by native.filegroup() in macros, new
`generator_{function,location,name}` fields are added to indicate
how it was generated.
''')
parser_target_dump.add_argument('target_set', metavar='TARGET_SET', nargs = '+', help='Set of targets to dump.')
parser_target_dump.set_defaults(func=cmd_target_dump)
# The target_commands command.
parser_actions = subparsers.add_parser(
'actions',
help='Dump action commands of Bazel targets.',
formatter_class=argparse.RawDescriptionHelpFormatter,
description=r'''
Print the action commands of a target, or set of targets, omitting
from the list of inputs sysroot and toolchain related headers, which
can be _very_ long when using the Fuchsia C++ toolchain.
''')
parser_actions.add_argument('target_set', metavar='TARGET_SET', nargs = '+', help='Set to targets to print actions for.')
parser_actions.add_argument('--', dest='extra_args', default=[], nargs=argparse.REMAINDER, help='extra Bazel aquery-compatible arguments.')
parser_actions.set_defaults(func=cmd_actions)
args = parser.parse_args()
if args.workspace:
os.chdir(args.workspace)
return args.func(args)
if __name__ == "__main__":
sys.exit(main())