blob: 1b2a32c99f7ccfba6f1dcc5ea6a580a3fd10b7b6 [file] [log] [blame]
Simon Glass22ce6412023-11-04 10:25:20 -06001# SPDX-License-Identifier: GPL-2.0+
2#
3# Copyright 2023 Google LLC
4#
5
6"""Handles parsing of buildman arguments
7
8This creates the argument parser and uses it to parse the arguments passed in
9"""
10
11import argparse
12import os
13import pathlib
14import sys
15
Simon Glassba1b3b92025-02-09 14:26:00 -070016from u_boot_pylib import gitutil
Simon Glass44ecdd42025-05-08 06:21:33 +020017from patman import project
Simon Glass22ce6412023-11-04 10:25:20 -060018from patman import settings
19
20PATMAN_DIR = pathlib.Path(__file__).parent
21HAS_TESTS = os.path.exists(PATMAN_DIR / "func_test.py")
22
Simon Glass22ce6412023-11-04 10:25:20 -060023
Simon Glassd9ec7472025-05-10 13:05:07 +020024class ErrorCatchingArgumentParser(argparse.ArgumentParser):
25 def __init__(self, **kwargs):
26 self.exit_state = None
27 self.catch_error = False
28 super().__init__(**kwargs)
29
30 def error(self, message):
31 if self.catch_error:
32 self.message = message
33 else:
34 super().error(message)
35
36 def exit(self, status=0, message=None):
37 if self.catch_error:
38 self.exit_state = True
39 else:
40 super().exit(status, message)
41
42
Simon Glass44ecdd42025-05-08 06:21:33 +020043def add_send_args(par):
44 """Add arguments for the 'send' command
Simon Glass22ce6412023-11-04 10:25:20 -060045
Simon Glass44ecdd42025-05-08 06:21:33 +020046 Arguments:
47 par (ArgumentParser): Parser to add to
Simon Glasse55a11c2025-05-08 06:09:46 +020048 """
Simon Glass44ecdd42025-05-08 06:21:33 +020049 par.add_argument(
50 '-c', '--count', dest='count', type=int, default=-1,
51 help='Automatically create patches from top n commits')
52 par.add_argument(
53 '-e', '--end', type=int, default=0,
54 help='Commits to skip at end of patch list')
55 par.add_argument(
Simon Glasse55a11c2025-05-08 06:09:46 +020056 '-i', '--ignore-errors', action='store_true',
57 dest='ignore_errors', default=False,
58 help='Send patches email even if patch errors are found')
Simon Glass44ecdd42025-05-08 06:21:33 +020059 par.add_argument(
Simon Glasse55a11c2025-05-08 06:09:46 +020060 '-l', '--limit-cc', dest='limit', type=int, default=None,
61 help='Limit the cc list to LIMIT entries [default: %(default)s]')
Simon Glass44ecdd42025-05-08 06:21:33 +020062 par.add_argument(
Simon Glasse55a11c2025-05-08 06:09:46 +020063 '-m', '--no-maintainers', action='store_false',
64 dest='add_maintainers', default=True,
65 help="Don't cc the file maintainers automatically")
Simon Glass0e43cad2025-05-10 13:04:55 +020066 default_arg = None
67 top_level = gitutil.get_top_level()
68 if top_level:
69 default_arg = os.path.join(top_level, 'scripts',
70 'get_maintainer.pl') + ' --norolestats'
Simon Glass44ecdd42025-05-08 06:21:33 +020071 par.add_argument(
Simon Glass22ce6412023-11-04 10:25:20 -060072 '--get-maintainer-script', dest='get_maintainer_script', type=str,
73 action='store',
Simon Glass0e43cad2025-05-10 13:04:55 +020074 default=default_arg,
Simon Glass22ce6412023-11-04 10:25:20 -060075 help='File name of the get_maintainer.pl (or compatible) script.')
Simon Glass44ecdd42025-05-08 06:21:33 +020076 par.add_argument(
77 '-r', '--in-reply-to', type=str, action='store',
78 help="Message ID that this series is in reply to")
79 par.add_argument(
80 '-s', '--start', dest='start', type=int, default=0,
81 help='Commit to start creating patches from (0 = HEAD)')
82 par.add_argument(
83 '-t', '--ignore-bad-tags', action='store_true', default=False,
84 help='Ignore bad tags / aliases (default=warn)')
85 par.add_argument(
86 '--no-binary', action='store_true', dest='ignore_binary',
87 default=False,
88 help="Do not output contents of changes in binary files")
89 par.add_argument(
90 '--no-check', action='store_false', dest='check_patch', default=True,
91 help="Don't check for patch compliance")
92 par.add_argument(
Simon Glass22ce6412023-11-04 10:25:20 -060093 '--tree', dest='check_patch_use_tree', default=False,
94 action='store_true',
95 help=("Set `tree` to True. If `tree` is False then we'll pass "
96 "'--no-tree' to checkpatch (default: tree=%(default)s)"))
Simon Glass44ecdd42025-05-08 06:21:33 +020097 par.add_argument(
98 '--no-tree', dest='check_patch_use_tree', action='store_false',
99 help="Set `tree` to False")
100 par.add_argument(
Simon Glass22ce6412023-11-04 10:25:20 -0600101 '--no-tags', action='store_false', dest='process_tags', default=True,
102 help="Don't process subject tags as aliases")
Simon Glass44ecdd42025-05-08 06:21:33 +0200103 par.add_argument(
104 '--no-signoff', action='store_false', dest='add_signoff',
105 default=True, help="Don't add Signed-off-by to patches")
106 par.add_argument(
107 '--smtp-server', type=str,
108 help="Specify the SMTP server to 'git send-email'")
109 par.add_argument(
110 '--keep-change-id', action='store_true',
111 help='Preserve Change-Id tags in patches to send.')
Simon Glass22ce6412023-11-04 10:25:20 -0600112
Simon Glass44ecdd42025-05-08 06:21:33 +0200113
Simon Glasse9f14cd2025-05-10 13:05:08 +0200114def _add_show_comments(parser):
115 parser.add_argument('-c', '--show-comments', action='store_true',
116 help='Show comments from each patch')
117
118
Simon Glass44ecdd42025-05-08 06:21:33 +0200119def add_send_subparser(subparsers):
120 """Add the 'send' subparser
121
122 Args:
123 subparsers (argparse action): Subparser parent
124
125 Return:
126 ArgumentParser: send subparser
127 """
128 send = subparsers.add_parser(
129 'send', help='Format, check and email patches (default command)')
130 send.add_argument(
131 '-b', '--branch', type=str,
132 help="Branch to process (by default, the current branch)")
133 send.add_argument(
134 '-n', '--dry-run', action='store_true', dest='dry_run',
135 default=False, help="Do a dry run (create but don't email patches)")
136 send.add_argument(
137 '--cc-cmd', dest='cc_cmd', type=str, action='store',
138 default=None, help='Output cc list for patch file (used by git)')
139 add_send_args(send)
Simon Glass22ce6412023-11-04 10:25:20 -0600140 send.add_argument('patchfiles', nargs='*')
Simon Glasse55a11c2025-05-08 06:09:46 +0200141 return send
Simon Glass22ce6412023-11-04 10:25:20 -0600142
Simon Glasse55a11c2025-05-08 06:09:46 +0200143
144def add_status_subparser(subparsers):
145 """Add the 'status' subparser
146
147 Args:
148 subparsers (argparse action): Subparser parent
Simon Glass22ce6412023-11-04 10:25:20 -0600149
Simon Glasse55a11c2025-05-08 06:09:46 +0200150 Return:
151 ArgumentParser: status subparser
152 """
Simon Glass22ce6412023-11-04 10:25:20 -0600153 status = subparsers.add_parser('status',
154 help='Check status of patches in patchwork')
Simon Glasse9f14cd2025-05-10 13:05:08 +0200155 _add_show_comments(status)
Simon Glass22ce6412023-11-04 10:25:20 -0600156 status.add_argument(
157 '-d', '--dest-branch', type=str,
158 help='Name of branch to create with collected responses')
159 status.add_argument('-f', '--force', action='store_true',
160 help='Force overwriting an existing branch')
Simon Glass802eeea2025-04-29 07:22:25 -0600161 status.add_argument('-T', '--single-thread', action='store_true',
162 help='Disable multithreading when reading patchwork')
Simon Glasse55a11c2025-05-08 06:09:46 +0200163 return status
164
165
166def setup_parser():
167 """Set up command-line parser
168
169 Returns:
170 argparse.Parser object
171 """
172 epilog = '''Create patches from commits in a branch, check them and email
173 them as specified by tags you place in the commits. Use -n to do a dry
174 run first.'''
175
Simon Glassd9ec7472025-05-10 13:05:07 +0200176 parser = ErrorCatchingArgumentParser(epilog=epilog)
Simon Glass44ecdd42025-05-08 06:21:33 +0200177 parser.add_argument(
178 '-D', '--debug', action='store_true',
Simon Glasse55a11c2025-05-08 06:09:46 +0200179 help='Enabling debugging (provides a full traceback on error)')
180 parser.add_argument(
181 '-N', '--no-capture', action='store_true',
182 help='Disable capturing of console output in tests')
183 parser.add_argument('-p', '--project', default=project.detect_project(),
184 help="Project name; affects default option values and "
185 "aliases [default: %(default)s]")
186 parser.add_argument('-P', '--patchwork-url',
187 default='https://patchwork.ozlabs.org',
188 help='URL of patchwork server [default: %(default)s]')
Simon Glass44ecdd42025-05-08 06:21:33 +0200189 parser.add_argument(
190 '-T', '--thread', action='store_true', dest='thread',
191 default=False, help='Create patches as a single thread')
Simon Glasse55a11c2025-05-08 06:09:46 +0200192 parser.add_argument(
193 '-v', '--verbose', action='store_true', dest='verbose', default=False,
194 help='Verbose output of errors and warnings')
195 parser.add_argument(
196 '-X', '--test-preserve-dirs', action='store_true',
197 help='Preserve and display test-created directories')
198 parser.add_argument(
199 '-H', '--full-help', action='store_true', dest='full_help',
200 default=False, help='Display the README file')
201
202 subparsers = parser.add_subparsers(dest='cmd')
203 add_send_subparser(subparsers)
204 add_status_subparser(subparsers)
205
206 # Only add the 'test' action if the test data files are available.
207 if HAS_TESTS:
208 test_parser = subparsers.add_parser('test', help='Run tests')
209 test_parser.add_argument('testname', type=str, default=None, nargs='?',
210 help="Specify the test to run")
211
Simon Glass65a45792025-05-10 13:05:09 +0200212 parsers = {
213 'main': parser,
214 }
215 return parsers
Simon Glass8b521cf2025-05-08 05:58:10 +0200216
217
Simon Glass65a45792025-05-10 13:05:09 +0200218def parse_args(argv=None, config_fname=None, parsers=None):
Simon Glass8b521cf2025-05-08 05:58:10 +0200219 """Parse command line arguments from sys.argv[]
220
221 Args:
222 argv (str or None): Arguments to process, or None to use sys.argv[1:]
223 config_fname (str): Config file to read, or None for default, or False
224 for an empty config
225
226 Returns:
227 tuple containing:
228 options: command line options
229 args: command lin arguments
230 """
Simon Glass65a45792025-05-10 13:05:09 +0200231 if not parsers:
232 parsers = setup_parser()
233 parser = parsers['main']
Simon Glass22ce6412023-11-04 10:25:20 -0600234
235 # Parse options twice: first to get the project and second to handle
236 # defaults properly (which depends on project)
237 # Use parse_known_args() in case 'cmd' is omitted
Simon Glass8b521cf2025-05-08 05:58:10 +0200238 if not argv:
239 argv = sys.argv[1:]
240
Simon Glass22ce6412023-11-04 10:25:20 -0600241 args, rest = parser.parse_known_args(argv)
242 if hasattr(args, 'project'):
Simon Glass8b521cf2025-05-08 05:58:10 +0200243 settings.Setup(parser, args.project, argv, config_fname)
Simon Glass22ce6412023-11-04 10:25:20 -0600244 args, rest = parser.parse_known_args(argv)
245
246 # If we have a command, it is safe to parse all arguments
247 if args.cmd:
248 args = parser.parse_args(argv)
Simon Glassb8a14f92025-05-08 06:30:14 +0200249 elif not args.full_help:
Simon Glass22ce6412023-11-04 10:25:20 -0600250 # No command, so insert it after the known arguments and before the ones
251 # that presumably relate to the 'send' subcommand
252 nargs = len(rest)
253 argv = argv[:-nargs] + ['send'] + rest
254 args = parser.parse_args(argv)
255
256 return args