mirror of
git://nv-tegra.nvidia.com/linux-nvgpu.git
synced 2025-12-22 09:12:24 +03:00
Open source GPL/LGPL release
This commit is contained in:
7101
scripts/checkpatch.pl
Executable file
7101
scripts/checkpatch.pl
Executable file
File diff suppressed because it is too large
Load Diff
68
scripts/const_structs.checkpatch
Normal file
68
scripts/const_structs.checkpatch
Normal file
@@ -0,0 +1,68 @@
|
||||
acpi_dock_ops
|
||||
address_space_operations
|
||||
backlight_ops
|
||||
block_device_operations
|
||||
clk_ops
|
||||
comedi_lrange
|
||||
component_ops
|
||||
dentry_operations
|
||||
dev_pm_ops
|
||||
dma_map_ops
|
||||
driver_info
|
||||
drm_connector_funcs
|
||||
drm_encoder_funcs
|
||||
drm_encoder_helper_funcs
|
||||
ethtool_ops
|
||||
extent_io_ops
|
||||
file_lock_operations
|
||||
file_operations
|
||||
hv_ops
|
||||
ide_dma_ops
|
||||
ide_port_ops
|
||||
inode_operations
|
||||
intel_dvo_dev_ops
|
||||
irq_domain_ops
|
||||
item_operations
|
||||
iwl_cfg
|
||||
iwl_ops
|
||||
kgdb_arch
|
||||
kgdb_io
|
||||
kset_uevent_ops
|
||||
lock_manager_operations
|
||||
machine_desc
|
||||
microcode_ops
|
||||
mlxsw_reg_info
|
||||
mtrr_ops
|
||||
neigh_ops
|
||||
net_device_ops
|
||||
nlmsvc_binding
|
||||
nvkm_device_chip
|
||||
of_device_id
|
||||
pci_raw_ops
|
||||
phy_ops
|
||||
pinctrl_ops
|
||||
pinmux_ops
|
||||
pipe_buf_operations
|
||||
platform_hibernation_ops
|
||||
platform_suspend_ops
|
||||
proto_ops
|
||||
regmap_access_table
|
||||
regulator_ops
|
||||
rpc_pipe_ops
|
||||
rtc_class_ops
|
||||
sd_desc
|
||||
seq_operations
|
||||
sirfsoc_padmux
|
||||
snd_ac97_build_ops
|
||||
snd_soc_component_driver
|
||||
soc_pcmcia_socket_ops
|
||||
stacktrace_ops
|
||||
sysfs_ops
|
||||
tty_operations
|
||||
uart_ops
|
||||
usb_mon_operations
|
||||
v4l2_ctrl_ops
|
||||
v4l2_ioctl_ops
|
||||
vm_operations_struct
|
||||
wacom_features
|
||||
wd_ops
|
||||
132
scripts/nvgpu_debug_hal.py
Executable file
132
scripts/nvgpu_debug_hal.py
Executable file
@@ -0,0 +1,132 @@
|
||||
#!/usr/bin/env python3
|
||||
#
|
||||
# Copyright (c) 2017-2018, NVIDIA CORPORATION. All rights reserved.
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
# usage: nvgpu_debug_hal.py [-h] [--csv] [--gk20a GK20A] [gops_filename]
|
||||
#
|
||||
# Analyze the HAL debugfs interface's output. With no arguments, prints out
|
||||
# statistics on the gpu_ops struct based on analysis of gk20a.h
|
||||
#
|
||||
# positional arguments:
|
||||
# gops_filename debugfs interface output file (from /d/gpu.0/hal/gops)
|
||||
#
|
||||
# optional arguments:
|
||||
# -h, --help show this help message and exit
|
||||
# --csv csv formatted output
|
||||
# --gk20a GK20A path to gk20a.h
|
||||
|
||||
import argparse
|
||||
import re
|
||||
from os import environ
|
||||
|
||||
description_str = ('Analyze the HAL debugfs interface\'s output. '
|
||||
'With no arguments, prints out statistics on the gpu_ops struct based on '
|
||||
'analysis of gk20a.h')
|
||||
|
||||
parser = argparse.ArgumentParser(description=description_str);
|
||||
parser.add_argument("--csv", help="csv formatted output", action="store_true");
|
||||
parser.add_argument("--gk20a", help="path to gk20a.h");
|
||||
parser.add_argument("gops_filename", help="debugfs interface output file (from /d/gpu.0/hal/gops)", nargs='?');
|
||||
args = parser.parse_args();
|
||||
|
||||
if args.gk20a:
|
||||
gk20a_h_path = args.gk20a
|
||||
else:
|
||||
top = environ.get('TOP');
|
||||
if top is None:
|
||||
print("$TOP is undefined, unable to find gk20a.h");
|
||||
exit(-1);
|
||||
gk20a_h_path = top + "/kernel/nvgpu/drivers/gpu/nvgpu/include/nvgpu/gk20a.h"
|
||||
|
||||
def get_function_pointer_name(line):
|
||||
matches = re.search('.*\(\*(?P<function_name>\w+)\)\(', line);
|
||||
if matches is None:
|
||||
return None
|
||||
else:
|
||||
return matches.group("function_name");
|
||||
|
||||
# Build the list of gpu_ops member function pointers from gk20a.h
|
||||
non_function_pointer_members = [];
|
||||
formatted_members = [];
|
||||
gops_members = dict();
|
||||
substruct_names = [];
|
||||
lone_members = [];
|
||||
with open(gk20a_h_path) as gk20a_h:
|
||||
# Skip to start of gpu_ops struct
|
||||
while gk20a_h.readline() != "struct gpu_ops {\n":
|
||||
continue;
|
||||
|
||||
line = gk20a_h.readline();
|
||||
while line != "};\n":
|
||||
# If this is a substruct
|
||||
if re.match('\t+struct.+\{', line):
|
||||
# Read the contents of the substruct
|
||||
line = gk20a_h.readline();
|
||||
struct_contents = ""
|
||||
while not re.match("\t*\} (\w+);", line):
|
||||
struct_contents += line;
|
||||
line = gk20a_h.readline();
|
||||
# Split out the substruct name and the function pointer names
|
||||
struct_name = re.match("\t*\} (?P<struct_name>\w+);", line).group("struct_name");
|
||||
struct_members = re.findall(r".+?\(\s*\*\s*(\w+)\s*\).+?;", struct_contents, flags=re.DOTALL)
|
||||
|
||||
# Store the substruct as an entry
|
||||
substruct_names.append(struct_name);
|
||||
gops_members[struct_name] = struct_members;
|
||||
# Format members
|
||||
for member in struct_members:
|
||||
formatted_members.append(struct_name + "." + member);
|
||||
else:
|
||||
# Lone members (function pointers or stuff not in a substruct)
|
||||
match = re.match(".*\(\*(?P<function_name>\w+)\)\(", line);
|
||||
if match is not None:
|
||||
# It's a function pointer, keep track of it
|
||||
lone_members.append(match.group("function_name"));
|
||||
formatted_members.append(match.group("function_name"));
|
||||
else:
|
||||
# Not a function pointer, may also catch comments etc.
|
||||
non_function_pointer_members.append(line.strip());
|
||||
line = gk20a_h.readline();
|
||||
if args.gops_filename:
|
||||
# Interpret gops file
|
||||
with open(args.gops_filename) as gops:
|
||||
i = 0;
|
||||
# Option for csv output
|
||||
if args.csv:
|
||||
format_string = '{0},{1}';
|
||||
else:
|
||||
format_string = '{0:<60} = {1}';
|
||||
for line in gops:
|
||||
print(format_string.format(formatted_members[i], line[:-1]));
|
||||
i += 1;
|
||||
else:
|
||||
# Just print some stats on the gpu_ops struct
|
||||
total = 0;
|
||||
print("----- Lone Function Pointers -----");
|
||||
print("Count =", len(lone_members));
|
||||
total += len(lone_members);
|
||||
for line in lone_members:
|
||||
print(line);
|
||||
print("----- Substruct Counts -----");
|
||||
for name in substruct_names:
|
||||
print(name, "=", len(gops_members[name]));
|
||||
total += len(gops_members[name])
|
||||
print("\n Total =", total);
|
||||
502
scripts/rfr
Executable file
502
scripts/rfr
Executable file
@@ -0,0 +1,502 @@
|
||||
#!/usr/bin/env python3
|
||||
#
|
||||
# Copyright (c) 2018-2020, NVIDIA CORPORATION. All Rights Reserved.
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
# Simple script for formatting RFR messages for nvgpu changes.
|
||||
#
|
||||
|
||||
import os
|
||||
import re
|
||||
import json
|
||||
import argparse
|
||||
import tempfile
|
||||
import subprocess
|
||||
import smtplib
|
||||
|
||||
from email.mime.text import MIMEText
|
||||
|
||||
from rfr_addrbook import rfr_ab_load
|
||||
from rfr_addrbook import rfr_ab_query
|
||||
from rfr_addrbook import rfr_ab_lookup
|
||||
|
||||
VERSION = '1.1.0'
|
||||
|
||||
# Default email address.
|
||||
to_addr = 'sw-mobile-nvgpu-core <sw-mobile-nvgpu-core@exchange.nvidia.com>'
|
||||
|
||||
# Gerrit commit URL formats. These are regular expressions to match the
|
||||
# incoming URLs against.
|
||||
gr_fmts = [ r'^(\d+)$',
|
||||
r'https?://git-master(?:\.nvidia\.com)?/r/(\d+)',
|
||||
r'https?://git-master(?:\.nvidia\.com)?/r/#/c/(\d+)/?',
|
||||
r'https?://git-master(?:\.nvidia\.com)?/r/c/(?:[\w\-_\/\.]+?)\+/(\d+)' ]
|
||||
|
||||
# The user to use. May be overridden but the default comes from the environment.
|
||||
user = os.environ['USER']
|
||||
|
||||
# Gerrit query command to obtain the patch URL. The substitution will be the
|
||||
# gerrit Change-ID parsed from the git commit message.
|
||||
gr_query_cmd = 'ssh %s@git-master -p 29418 gerrit query --format json --current-patch-set --files %s'
|
||||
|
||||
def parse_args():
|
||||
"""
|
||||
Parse arguments to rfr.
|
||||
"""
|
||||
|
||||
ep="""This program will format commit messages into something that can be
|
||||
sent to the nvgpu mailing list for review.
|
||||
"""
|
||||
help_msg="""Git or gerrit commits to describe. Can be either a git or gerrit
|
||||
commit ID. If the ID starts with a 'I' then it will be treated as a gerrit ID.
|
||||
If the commit ID looks like a gerrit URL then it is treated as a gerrit URL.
|
||||
Otherwise it's treated as a git commit ID.
|
||||
"""
|
||||
parser = argparse.ArgumentParser(description='RFR formatting tool',
|
||||
epilog=ep)
|
||||
|
||||
parser.add_argument('-V', '--version', action='store_true', default=False,
|
||||
help='print the program version')
|
||||
parser.add_argument('-m', '--msg', action='store', default=None,
|
||||
help='Custom message to add to the RFR email')
|
||||
parser.add_argument('-N', '--no-msg', action='store_true', default=False,
|
||||
help='Force no message in output.')
|
||||
parser.add_argument('-e', '--email', action='store_true', default=False,
|
||||
help='Use direct email to post the RFR.')
|
||||
parser.add_argument('-s', '--subject', action='store', default=None,
|
||||
help='Specify the email\'s subject.')
|
||||
parser.add_argument('-R', '--sender', action='store', default=None,
|
||||
help='Specify a special from: email address.')
|
||||
parser.add_argument('-t', '--to', action='append', default=[],
|
||||
help='Specify an additional To: email address.'
|
||||
'Can be repeated (-t a -t b).')
|
||||
parser.add_argument('-c', '--cc', action='append', default=[],
|
||||
help='Specify an additional Cc: email address.'
|
||||
'Can be repeated (-c a -c b).')
|
||||
parser.add_argument('-F', '--file', action='store', default=None,
|
||||
help='File with commits, one per line')
|
||||
parser.add_argument('-q', '--query-addrbook', action='store', default=None,
|
||||
nargs='?', const='.', metavar='regex',
|
||||
help='Regex query for address book')
|
||||
parser.add_argument('-a', '--addrbook', action='store', default=None,
|
||||
metavar='addrbook',
|
||||
help='Specify location to look for address book')
|
||||
parser.add_argument('-I', '--ignore-addrbook', action='store_true',
|
||||
default=False,
|
||||
help='Ignore address book lookup. Default is false.')
|
||||
parser.add_argument('--no-default-addr', action='store_true',
|
||||
default=False,
|
||||
help='Do not use the default address.')
|
||||
|
||||
# Positionals: the gerrit URLs.
|
||||
parser.add_argument('commits', metavar='Commit-IDs',
|
||||
nargs='*',
|
||||
help=help_msg)
|
||||
|
||||
arg_parser = parser.parse_args()
|
||||
|
||||
return arg_parser
|
||||
|
||||
def get_gerrit_url_id(cmt):
|
||||
"""
|
||||
Determines if the passed cmt is a gerrit commit URL. If it is then this
|
||||
returns the URL ID; otherwise it returns None.
|
||||
"""
|
||||
|
||||
for fmt in gr_fmts:
|
||||
p = re.compile(fmt)
|
||||
m = p.search(cmt)
|
||||
if m:
|
||||
return m.group(1)
|
||||
|
||||
return None
|
||||
|
||||
def read_commit_file(filp):
|
||||
"""
|
||||
Read a file full of commits and return a list of those commits.
|
||||
"""
|
||||
|
||||
commits = [ ]
|
||||
|
||||
for line in filp:
|
||||
|
||||
line = line.strip()
|
||||
|
||||
# Skip empty lines and lines that start with a '#'.
|
||||
if line.find('#') == 0 or line == '':
|
||||
continue
|
||||
|
||||
# Otherwise append the line to the list of commits. This will append
|
||||
# all the text, so in cases like:
|
||||
#
|
||||
# http://git-master/r/1540705 - my commit
|
||||
#
|
||||
# Anything after the first space in the line is ignored. This lets you
|
||||
# add some descriptive text after the commit.
|
||||
commits.append(line.split()[0])
|
||||
|
||||
return commits
|
||||
|
||||
def gerrit_query(change):
|
||||
"""
|
||||
Query gerrit for the JSON change information. Return a python object
|
||||
describing the JSON data.
|
||||
|
||||
change can either be a Change-Id or the numeric change number from a URL.
|
||||
|
||||
Note there is an interesting limitation with this: gerrit can have multiple
|
||||
changes with the same Change-Id (./sigh). So if you query a change ID that
|
||||
points to multiple changes you get back all of them.
|
||||
|
||||
This script just uses the first. Ideally one could filter by branch or by
|
||||
some other distinguishing factor.
|
||||
"""
|
||||
|
||||
query_cmd = gr_query_cmd % (user, change)
|
||||
|
||||
prog = subprocess.Popen(query_cmd, shell=True,
|
||||
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
|
||||
stdout_data, stderr_data = prog.communicate()
|
||||
if prog.returncode != 0:
|
||||
print('`%s\' failed!' % query_cmd)
|
||||
return False
|
||||
|
||||
commit = json.loads(stdout_data.decode('utf-8').splitlines()[0])
|
||||
if 'id' not in commit:
|
||||
print('%s is not a gerrit commit!?' % change)
|
||||
print('Most likely you need to push the change.')
|
||||
return None
|
||||
|
||||
return commit
|
||||
|
||||
def commit_info_from_gerrit_change_id(change_id):
|
||||
"""
|
||||
Return a dict with all the gerrit info from a gerrit change ID.
|
||||
"""
|
||||
|
||||
return gerrit_query(change_id)
|
||||
|
||||
def commit_info_from_git_sha1(rev):
|
||||
"""
|
||||
Return a dict with all the gerrit info from a git sha1 rev.
|
||||
"""
|
||||
|
||||
return gerrit_query(rev)
|
||||
|
||||
def commit_info_from_gerrit_cl(cmt):
|
||||
"""
|
||||
Return a dict with all the gerrit info from a Gerrit URL.
|
||||
"""
|
||||
|
||||
cl = get_gerrit_url_id(cmt)
|
||||
if not cl:
|
||||
return None
|
||||
|
||||
return gerrit_query(cl)
|
||||
|
||||
def git_sha1_from_commit(commit_ish):
|
||||
"""
|
||||
Return sha1 revision from a commit-ish string, or None if this doesn't
|
||||
appear to be a commit.
|
||||
"""
|
||||
|
||||
prog = subprocess.Popen('git rev-parse %s' % commit_ish, shell=True,
|
||||
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
|
||||
stdout_data, stderr_data = prog.communicate()
|
||||
if prog.returncode != 0:
|
||||
print('`git rev-parse %s\' failed?!' % commit_ish)
|
||||
return None
|
||||
|
||||
return stdout_data.decode('utf-8')
|
||||
|
||||
def get_user_message(start_msg):
|
||||
"""
|
||||
Get a user message from the user. This is done with the default text
|
||||
editor.
|
||||
"""
|
||||
|
||||
editor = os.environ.get('EDITOR', 'vim')
|
||||
|
||||
with tempfile.NamedTemporaryFile(suffix=".tmp") as filp:
|
||||
filp.write(start_msg.encode('utf-8'))
|
||||
filp.flush()
|
||||
|
||||
edit_cmd = editor + ' ' + filp.name
|
||||
|
||||
try:
|
||||
subprocess.call(edit_cmd.split())
|
||||
except:
|
||||
print('Failed to run editor: `%s\'' % edit_cmd)
|
||||
return None
|
||||
|
||||
filp.seek(0)
|
||||
lines = filp.readlines()
|
||||
|
||||
return ''.join([l.decode('utf-8') for l in lines if not l.startswith(b'#')])
|
||||
|
||||
def indent_lines(text, ind):
|
||||
"""
|
||||
Prepend each new line in the passed text with ind.
|
||||
"""
|
||||
return ''.join(ind + l + '\n' for l in text.splitlines())
|
||||
|
||||
def format_commits(commits_info, extra_message):
|
||||
"""
|
||||
Takes a list of the commit info objects and returns a string with the
|
||||
text to print. This can either be directly printed or emailed.
|
||||
"""
|
||||
|
||||
whole_template = """Hi All,
|
||||
|
||||
I would like you to review the following changes.
|
||||
{extra_message}
|
||||
{cmt_descriptions}
|
||||
Thanks!
|
||||
{cmt_verbose}"""
|
||||
|
||||
cmt_template = """
|
||||
+----------------------------------------
|
||||
| {url}
|
||||
| Author: {author}
|
||||
|
||||
{cmtmsg}
|
||||
{files}
|
||||
"""
|
||||
|
||||
cmt_descriptions = ''
|
||||
for c in commits_info:
|
||||
cmt_descriptions += " %s - %s\n" % (c['url'], c['subject'])
|
||||
|
||||
# Add new lines around the extra_message, if applicable. Otherwise we don't
|
||||
# want anything to show up for extra_message.
|
||||
if extra_message:
|
||||
extra_message = '\n%s\n' % extra_message
|
||||
else:
|
||||
extra_message = ''
|
||||
|
||||
cmt_verbose = ''
|
||||
for c in commits_info:
|
||||
# note: x['deletions'] is negative (or zero)
|
||||
files = c['currentPatchSet']['files']
|
||||
file_widths = [
|
||||
len(x['file']) for x in files if x['file'] != '/COMMIT_MSG']
|
||||
|
||||
# For merge commits there are no files in the commit itself. So this
|
||||
# causes the below max() operation to choke. Given that there are no
|
||||
# files it makes little sense for us to try and fill in any file
|
||||
# details!
|
||||
if file_widths:
|
||||
width = max(file_widths)
|
||||
file_template = ("{name: <{width}} | " +
|
||||
"{sum:<3} (+{adds: <3} -{dels: <3})")
|
||||
files_changed_desc = [file_template.format(
|
||||
name=x['file'],
|
||||
width=width,
|
||||
sum=x['insertions'] - x['deletions'],
|
||||
adds=x['insertions'],
|
||||
dels=-x['deletions'])
|
||||
for x in files if x['file'] != '/COMMIT_MSG'
|
||||
]
|
||||
else:
|
||||
files_changed_desc = ''
|
||||
|
||||
cmt_verbose += cmt_template.format(url=c['url'],
|
||||
author=c['owner']['name'],
|
||||
cmtmsg=indent_lines(
|
||||
c['commitMessage'], ' '),
|
||||
files=' ' + "\n ".join(
|
||||
files_changed_desc))
|
||||
|
||||
return whole_template.format(cmt_descriptions=cmt_descriptions,
|
||||
extra_message=extra_message,
|
||||
cmt_verbose=cmt_verbose)
|
||||
|
||||
def format_msg_comment(subject, to_addrs, cc_addrs):
|
||||
"""
|
||||
Take the list of to_addrs and format them into a comment so that the sender
|
||||
knows who they are sending an email to.
|
||||
"""
|
||||
|
||||
msg_comment = """# RFR commit editor
|
||||
#
|
||||
# Lines that begin with a '#' are comments and will be ignored in the final
|
||||
# message. '#' characters that appear else where in the line will be ignored.
|
||||
# White space is not stripped from lines so, for example, ' # blah' will not
|
||||
# be ignored!
|
||||
#
|
||||
"""
|
||||
|
||||
msg_comment += '# Subject: %s\n' % subject
|
||||
|
||||
for addr in to_addrs:
|
||||
msg_comment += '# To: %s\n' % addr
|
||||
for addr in cc_addrs:
|
||||
msg_comment += '# Cc: %s\n' % addr
|
||||
|
||||
return msg_comment
|
||||
|
||||
def display_commits(commits_info, extra_message):
|
||||
"""
|
||||
Print the list of commits to the directly to the terminal.
|
||||
"""
|
||||
|
||||
print(format_commits(commits_info, extra_message))
|
||||
|
||||
def email_commits(commits_info, sender, subject, args):
|
||||
"""
|
||||
Directly email commits to the nvgpu-core mailing list for review!
|
||||
"""
|
||||
|
||||
# Lets you drop nvgpu-core from the to-addr if you desire. You can then
|
||||
# add it back to CC if you wish.
|
||||
if not args.no_default_addr:
|
||||
args.to.append(to_addr)
|
||||
|
||||
to_addrs = rfr_ab_lookup(args.to, args.ignore_addrbook)
|
||||
if to_addrs == None:
|
||||
print('Junk address: aborting!')
|
||||
print('Use `-I\' to ignore.')
|
||||
return
|
||||
cc_addrs = rfr_ab_lookup(args.cc, args.ignore_addrbook)
|
||||
if cc_addrs == None:
|
||||
print('Junk address: aborting!')
|
||||
print('Use `-I\' to ignore.')
|
||||
return
|
||||
|
||||
if args.no_msg:
|
||||
args.msg = None
|
||||
|
||||
body = format_msg_comment(subject, to_addrs, cc_addrs) + \
|
||||
format_commits(commits_info, args.msg)
|
||||
|
||||
# Most people like to write a little description of their patch series
|
||||
# in the email body. This aims to take care of that.
|
||||
#
|
||||
# If arg_parser.msg is set then we can just use that. If there's no
|
||||
# args.msg field then we will try to fire up a text editor and let the
|
||||
# user write something we will copy into the email the same way as
|
||||
# args.msg. This makes it easier to a write paragraph - passing that much
|
||||
# text on the CLI is annoying!
|
||||
#
|
||||
# However, if the user decides no message is a must then they can use
|
||||
# the '--no-msg' argument to supress this message. This arg will also
|
||||
# supress a message supplied with '--msg'.
|
||||
|
||||
if not args.msg and not args.no_msg:
|
||||
text = get_user_message(body)
|
||||
|
||||
if not text or text.strip() == '':
|
||||
print('Empty user message: aborting!')
|
||||
return
|
||||
|
||||
msg = MIMEText(text)
|
||||
else:
|
||||
msg = MIMEText(body)
|
||||
|
||||
msg['To'] = ", ".join(to_addrs)
|
||||
msg['Cc'] = ", ".join(cc_addrs)
|
||||
msg['From'] = sender
|
||||
msg['Subject'] = subject
|
||||
|
||||
s = smtplib.SMTP('mail.nvidia.com')
|
||||
s.sendmail(sender,
|
||||
to_addrs,
|
||||
msg.as_string())
|
||||
s.quit()
|
||||
|
||||
def main():
|
||||
"""
|
||||
The magic happens here.
|
||||
"""
|
||||
|
||||
arg_parser = parse_args()
|
||||
commits_info = [ ]
|
||||
|
||||
if arg_parser.version:
|
||||
print('Version: %s' % VERSION)
|
||||
exit(0)
|
||||
|
||||
if arg_parser.addrbook:
|
||||
success = rfr_ab_load(arg_parser.addrbook)
|
||||
if not success:
|
||||
exit(1)
|
||||
else:
|
||||
rfr_ab_load(None)
|
||||
|
||||
if arg_parser.query_addrbook:
|
||||
rfr_ab_query(arg_parser.query_addrbook)
|
||||
exit(0)
|
||||
|
||||
if arg_parser.file:
|
||||
filp = open(arg_parser.file, 'r')
|
||||
|
||||
if arg_parser.commits:
|
||||
arg_parser.commits.extend(read_commit_file(filp))
|
||||
else:
|
||||
arg_parser.commits = read_commit_file(filp)
|
||||
|
||||
# Oops: no commits?
|
||||
if not arg_parser.commits or len(arg_parser.commits) == 0:
|
||||
print('No commits!')
|
||||
exit(-1)
|
||||
|
||||
# Builds a dictionary of Gerrit Change-Ids. From the Change-Ids we can then
|
||||
# get the commit message and URL.
|
||||
#
|
||||
# This also builds an array of those same commit IDs to track the ordering
|
||||
# of the commits so that the user can choose the order of the patches based
|
||||
# on the order in which they pass the commits.
|
||||
for cmt in arg_parser.commits:
|
||||
|
||||
if cmt[0] == 'I':
|
||||
info = commit_info_from_gerrit_change_id(cmt)
|
||||
elif get_gerrit_url_id(cmt):
|
||||
info = commit_info_from_gerrit_cl(cmt)
|
||||
else:
|
||||
info = commit_info_from_git_sha1(git_sha1_from_commit(cmt))
|
||||
|
||||
if info:
|
||||
commits_info.append(info)
|
||||
else:
|
||||
print('Warning: \'%s\' doesn\'t appear to be a commit!' % cmt)
|
||||
|
||||
if arg_parser.email:
|
||||
# If no subject was specified then use the first commit subject as
|
||||
# the subject.
|
||||
subject = arg_parser.subject
|
||||
if not subject:
|
||||
subject = '[RFR] %s' % commits_info[0]['subject']
|
||||
|
||||
# If no specified sender then use the name from the environment as
|
||||
# the sender (plus @nvidia.com). This arg is primarily useful for
|
||||
# people who do not have a username for the Linux machine that matches
|
||||
# their SSO ID.
|
||||
sender = arg_parser.sender
|
||||
if not sender:
|
||||
sender = '%s@nvidia.com' % user
|
||||
|
||||
email_commits(commits_info, sender, subject, arg_parser)
|
||||
else:
|
||||
display_commits(commits_info, arg_parser.msg)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
191
scripts/rfr_addrbook.py
Normal file
191
scripts/rfr_addrbook.py
Normal file
@@ -0,0 +1,191 @@
|
||||
# Copyright (c) 2019, NVIDIA CORPORATION. All Rights Reserved.
|
||||
#
|
||||
# Nvgpu address book. Entries in here let us do translations from human readable
|
||||
# email names/aliases to the real NV email addresses.
|
||||
#
|
||||
# This is structured as a dictionary with keys as human readable names which
|
||||
# point to the real email address.
|
||||
#
|
||||
# It's fine to have multiple keys pointing to the same thing. Aliases are nice.
|
||||
#
|
||||
|
||||
import re
|
||||
import os
|
||||
|
||||
# The table itself is private. Use the methods below to do AB lookups, etc.
|
||||
#
|
||||
# Also please note: the table should be all lower case! This makes it easy to
|
||||
# just lowercase all incoming queries so that we can do case insensitive
|
||||
# lookups.
|
||||
__rfr_address_book = { }
|
||||
|
||||
def __rfr_parse_addrbook(ab):
|
||||
"""
|
||||
Parse an address book. Ignore empty lines and lines that begin with '#'.
|
||||
All other lines are split by the '|' character into 2 strings - a key and
|
||||
a value. The key is a nickname and the value is the real address. For
|
||||
example:
|
||||
|
||||
alex | Alex Waterman <alexw@nvidia.com>
|
||||
|
||||
Note: if there's a syntax error detected an error is always printed. Unlike
|
||||
if there's missing files in which errors may be silent.
|
||||
|
||||
Returns True/False for pass/fail.
|
||||
"""
|
||||
|
||||
global __rfr_address_book
|
||||
|
||||
tmp_ab = { }
|
||||
line_nr = 0
|
||||
|
||||
for line in ab.readlines():
|
||||
line_nr += 1
|
||||
l = line.strip()
|
||||
|
||||
if len(l) == 0 or l[0] == '#':
|
||||
continue
|
||||
|
||||
# Only allow at most 1 split.
|
||||
kv = l.split('|', 1)
|
||||
|
||||
if len(kv) != 2:
|
||||
print('Error: unable to parse address book. Invalid line:')
|
||||
print(' > \'%s\' @ line: %d' % (line, line_nr))
|
||||
return False
|
||||
|
||||
tmp_ab[kv[0].strip().lower()] = kv[1].strip().lower()
|
||||
|
||||
__rfr_address_book = tmp_ab
|
||||
return True
|
||||
|
||||
def __rfr_load_ab(path, silent=False):
|
||||
"""
|
||||
Load the passed address book and print errors if silent is False.
|
||||
"""
|
||||
|
||||
success = True
|
||||
|
||||
try:
|
||||
with open(path) as ab:
|
||||
if not __rfr_parse_addrbook(ab):
|
||||
success = False
|
||||
except Exception as err:
|
||||
success = False
|
||||
if not silent:
|
||||
print('Error: %s' % err)
|
||||
|
||||
# It's not a very helpful error message I suppose. Eh. We will get more
|
||||
# detail from the __rfr_parse_addrbook() call itself.
|
||||
if not success and not silent:
|
||||
print('Failed to parse AB: %s' % path)
|
||||
|
||||
return success
|
||||
|
||||
def rfr_ab_load(ab_path):
|
||||
"""
|
||||
Attempt to load an address book.
|
||||
|
||||
If ab_path is None then look for the book at the environment variable
|
||||
RFR_ADDRBOOK. If that's not found or fails to load then fall back to trying
|
||||
~/.rfr-addrbook. If that doesn't work just silently return True and we will
|
||||
have an empty address book.
|
||||
|
||||
If ab_path is not None then try to load the addr book from the passed path
|
||||
and if there's an error print an error message. This will return the result
|
||||
of the AB load.
|
||||
"""
|
||||
|
||||
if ab_path:
|
||||
return __rfr_load_ab(ab_path)
|
||||
|
||||
# Ok, if we are here, do the whole env logic thing.
|
||||
ab_path = os.getenv('RFR_ADDRBOOK')
|
||||
if ab_path:
|
||||
success = __rfr_load_ab(ab_path, silent=True)
|
||||
if success:
|
||||
return True
|
||||
|
||||
# Otherwise... Try ~/.rfr-addrbook
|
||||
ab_path = os.getenv('HOME') + '/.rfr-addrbook'
|
||||
if ab_path:
|
||||
success = __rfr_load_ab(ab_path, silent=True)
|
||||
if success:
|
||||
return True
|
||||
|
||||
# Well, it all failed. But we don't care.
|
||||
return True
|
||||
|
||||
def rfr_ab_query(regex):
|
||||
"""
|
||||
Print a list of keys that match the passed regex string.
|
||||
"""
|
||||
|
||||
matches = [ ]
|
||||
|
||||
p = re.compile(regex, re.IGNORECASE)
|
||||
|
||||
for k in __rfr_address_book.keys():
|
||||
m = p.search(k)
|
||||
if not m:
|
||||
continue
|
||||
|
||||
matches.append(k)
|
||||
|
||||
print('Address book query: %s' % regex)
|
||||
|
||||
if not matches:
|
||||
print('> No matches')
|
||||
return
|
||||
|
||||
for addr in sorted(matches):
|
||||
print('> %-15s | %s' % (addr, __rfr_address_book[addr]))
|
||||
|
||||
def rfr_ab_lookup_single(addr):
|
||||
"""
|
||||
Exact lookup into the address book but if the addr isn't found in the keys
|
||||
then also check the values. If the addr is not found in the values then
|
||||
return None.
|
||||
|
||||
This lets the rfr script either ignore the AB lookup failure or bail out.
|
||||
"""
|
||||
|
||||
# If there's no address book, just pass the addr through.
|
||||
if len(list(__rfr_address_book.keys())) == 0:
|
||||
return addr
|
||||
|
||||
lc_addr = addr.lower()
|
||||
|
||||
if lc_addr in __rfr_address_book:
|
||||
return __rfr_address_book[lc_addr]
|
||||
|
||||
if lc_addr in list(__rfr_address_book.values()):
|
||||
# Return the orignal, un-lowercased.
|
||||
return addr
|
||||
|
||||
return None
|
||||
|
||||
def rfr_ab_lookup(addrs, ignore_missing):
|
||||
"""
|
||||
Take a list of addresses and look them up in the address book. Return a new
|
||||
list which contains the results of the lookups.
|
||||
|
||||
If ignore_missing is True then if there's an address book lookup failure
|
||||
just ignore it and pass the address through to the new list. If False then
|
||||
fail and bail (return None).
|
||||
"""
|
||||
|
||||
lookups = [ ]
|
||||
|
||||
for a in addrs:
|
||||
lookup = rfr_ab_lookup_single(a)
|
||||
if not lookup and not ignore_missing:
|
||||
print('Unknown address: %s' % a)
|
||||
return None
|
||||
|
||||
if lookup:
|
||||
lookups.append(lookup)
|
||||
else:
|
||||
lookups.append(a)
|
||||
|
||||
return lookups
|
||||
1536
scripts/spelling.txt
Normal file
1536
scripts/spelling.txt
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user