Simon Glass | 22ce641 | 2023-11-04 10:25:20 -0600 | [diff] [blame] | 1 | # SPDX-License-Identifier: GPL-2.0+ |
| 2 | # |
| 3 | # Copyright 2023 Google LLC |
| 4 | # |
| 5 | |
| 6 | """Handles parsing of buildman arguments |
| 7 | |
| 8 | This creates the argument parser and uses it to parse the arguments passed in |
| 9 | """ |
| 10 | |
| 11 | import argparse |
| 12 | import os |
| 13 | import pathlib |
| 14 | import sys |
| 15 | |
Simon Glass | ba1b3b9 | 2025-02-09 14:26:00 -0700 | [diff] [blame] | 16 | from u_boot_pylib import gitutil |
Simon Glass | 44ecdd4 | 2025-05-08 06:21:33 +0200 | [diff] [blame] | 17 | from patman import project |
Simon Glass | 22ce641 | 2023-11-04 10:25:20 -0600 | [diff] [blame] | 18 | from patman import settings |
| 19 | |
| 20 | PATMAN_DIR = pathlib.Path(__file__).parent |
| 21 | HAS_TESTS = os.path.exists(PATMAN_DIR / "func_test.py") |
| 22 | |
Simon Glass | 55d335f | 2025-05-10 13:05:13 +0200 | [diff] [blame] | 23 | # Aliases for subcommands |
| 24 | ALIASES = { |
Simon Glass | 5e52d17 | 2025-05-10 13:05:15 +0200 | [diff] [blame] | 25 | 'series': ['s', 'ser'], |
Simon Glass | 55d335f | 2025-05-10 13:05:13 +0200 | [diff] [blame] | 26 | 'status': ['st'], |
| 27 | 'patchwork': ['pw'], |
Simon Glass | 14bac32 | 2025-05-10 13:05:16 +0200 | [diff] [blame] | 28 | 'upstream': ['us'], |
Simon Glass | 5e52d17 | 2025-05-10 13:05:15 +0200 | [diff] [blame] | 29 | |
| 30 | # Series aliases |
| 31 | 'archive': ['ar'], |
| 32 | 'autolink': ['au'], |
| 33 | 'gather': ['g'], |
| 34 | 'open': ['o'], |
| 35 | 'progress': ['p', 'pr', 'prog'], |
| 36 | 'rm-version': ['rmv'], |
| 37 | 'unarchive': ['unar'], |
Simon Glass | 55d335f | 2025-05-10 13:05:13 +0200 | [diff] [blame] | 38 | } |
Simon Glass | 22ce641 | 2023-11-04 10:25:20 -0600 | [diff] [blame] | 39 | |
Simon Glass | 5e52d17 | 2025-05-10 13:05:15 +0200 | [diff] [blame] | 40 | |
Simon Glass | d9ec747 | 2025-05-10 13:05:07 +0200 | [diff] [blame] | 41 | class ErrorCatchingArgumentParser(argparse.ArgumentParser): |
| 42 | def __init__(self, **kwargs): |
| 43 | self.exit_state = None |
| 44 | self.catch_error = False |
| 45 | super().__init__(**kwargs) |
| 46 | |
| 47 | def error(self, message): |
| 48 | if self.catch_error: |
| 49 | self.message = message |
| 50 | else: |
| 51 | super().error(message) |
| 52 | |
| 53 | def exit(self, status=0, message=None): |
| 54 | if self.catch_error: |
| 55 | self.exit_state = True |
| 56 | else: |
| 57 | super().exit(status, message) |
| 58 | |
| 59 | |
Simon Glass | 44ecdd4 | 2025-05-08 06:21:33 +0200 | [diff] [blame] | 60 | def add_send_args(par): |
| 61 | """Add arguments for the 'send' command |
Simon Glass | 22ce641 | 2023-11-04 10:25:20 -0600 | [diff] [blame] | 62 | |
Simon Glass | 44ecdd4 | 2025-05-08 06:21:33 +0200 | [diff] [blame] | 63 | Arguments: |
| 64 | par (ArgumentParser): Parser to add to |
Simon Glass | e55a11c | 2025-05-08 06:09:46 +0200 | [diff] [blame] | 65 | """ |
Simon Glass | 44ecdd4 | 2025-05-08 06:21:33 +0200 | [diff] [blame] | 66 | par.add_argument( |
| 67 | '-c', '--count', dest='count', type=int, default=-1, |
| 68 | help='Automatically create patches from top n commits') |
| 69 | par.add_argument( |
| 70 | '-e', '--end', type=int, default=0, |
| 71 | help='Commits to skip at end of patch list') |
| 72 | par.add_argument( |
Simon Glass | e55a11c | 2025-05-08 06:09:46 +0200 | [diff] [blame] | 73 | '-i', '--ignore-errors', action='store_true', |
| 74 | dest='ignore_errors', default=False, |
| 75 | help='Send patches email even if patch errors are found') |
Simon Glass | 44ecdd4 | 2025-05-08 06:21:33 +0200 | [diff] [blame] | 76 | par.add_argument( |
Simon Glass | e55a11c | 2025-05-08 06:09:46 +0200 | [diff] [blame] | 77 | '-l', '--limit-cc', dest='limit', type=int, default=None, |
| 78 | help='Limit the cc list to LIMIT entries [default: %(default)s]') |
Simon Glass | 44ecdd4 | 2025-05-08 06:21:33 +0200 | [diff] [blame] | 79 | par.add_argument( |
Simon Glass | e55a11c | 2025-05-08 06:09:46 +0200 | [diff] [blame] | 80 | '-m', '--no-maintainers', action='store_false', |
| 81 | dest='add_maintainers', default=True, |
| 82 | help="Don't cc the file maintainers automatically") |
Simon Glass | 0e43cad | 2025-05-10 13:04:55 +0200 | [diff] [blame] | 83 | default_arg = None |
| 84 | top_level = gitutil.get_top_level() |
| 85 | if top_level: |
| 86 | default_arg = os.path.join(top_level, 'scripts', |
| 87 | 'get_maintainer.pl') + ' --norolestats' |
Simon Glass | 44ecdd4 | 2025-05-08 06:21:33 +0200 | [diff] [blame] | 88 | par.add_argument( |
Simon Glass | 22ce641 | 2023-11-04 10:25:20 -0600 | [diff] [blame] | 89 | '--get-maintainer-script', dest='get_maintainer_script', type=str, |
| 90 | action='store', |
Simon Glass | 0e43cad | 2025-05-10 13:04:55 +0200 | [diff] [blame] | 91 | default=default_arg, |
Simon Glass | 22ce641 | 2023-11-04 10:25:20 -0600 | [diff] [blame] | 92 | help='File name of the get_maintainer.pl (or compatible) script.') |
Simon Glass | 44ecdd4 | 2025-05-08 06:21:33 +0200 | [diff] [blame] | 93 | par.add_argument( |
| 94 | '-r', '--in-reply-to', type=str, action='store', |
| 95 | help="Message ID that this series is in reply to") |
| 96 | par.add_argument( |
| 97 | '-s', '--start', dest='start', type=int, default=0, |
| 98 | help='Commit to start creating patches from (0 = HEAD)') |
| 99 | par.add_argument( |
| 100 | '-t', '--ignore-bad-tags', action='store_true', default=False, |
| 101 | help='Ignore bad tags / aliases (default=warn)') |
| 102 | par.add_argument( |
| 103 | '--no-binary', action='store_true', dest='ignore_binary', |
| 104 | default=False, |
| 105 | help="Do not output contents of changes in binary files") |
| 106 | par.add_argument( |
| 107 | '--no-check', action='store_false', dest='check_patch', default=True, |
| 108 | help="Don't check for patch compliance") |
| 109 | par.add_argument( |
Simon Glass | 22ce641 | 2023-11-04 10:25:20 -0600 | [diff] [blame] | 110 | '--tree', dest='check_patch_use_tree', default=False, |
| 111 | action='store_true', |
| 112 | help=("Set `tree` to True. If `tree` is False then we'll pass " |
| 113 | "'--no-tree' to checkpatch (default: tree=%(default)s)")) |
Simon Glass | 44ecdd4 | 2025-05-08 06:21:33 +0200 | [diff] [blame] | 114 | par.add_argument( |
| 115 | '--no-tree', dest='check_patch_use_tree', action='store_false', |
| 116 | help="Set `tree` to False") |
| 117 | par.add_argument( |
Simon Glass | 22ce641 | 2023-11-04 10:25:20 -0600 | [diff] [blame] | 118 | '--no-tags', action='store_false', dest='process_tags', default=True, |
| 119 | help="Don't process subject tags as aliases") |
Simon Glass | 44ecdd4 | 2025-05-08 06:21:33 +0200 | [diff] [blame] | 120 | par.add_argument( |
| 121 | '--no-signoff', action='store_false', dest='add_signoff', |
| 122 | default=True, help="Don't add Signed-off-by to patches") |
| 123 | par.add_argument( |
| 124 | '--smtp-server', type=str, |
| 125 | help="Specify the SMTP server to 'git send-email'") |
| 126 | par.add_argument( |
| 127 | '--keep-change-id', action='store_true', |
| 128 | help='Preserve Change-Id tags in patches to send.') |
Simon Glass | 22ce641 | 2023-11-04 10:25:20 -0600 | [diff] [blame] | 129 | |
Simon Glass | 44ecdd4 | 2025-05-08 06:21:33 +0200 | [diff] [blame] | 130 | |
Simon Glass | e9f14cd | 2025-05-10 13:05:08 +0200 | [diff] [blame] | 131 | def _add_show_comments(parser): |
| 132 | parser.add_argument('-c', '--show-comments', action='store_true', |
| 133 | help='Show comments from each patch') |
| 134 | |
Simon Glass | 5e52d17 | 2025-05-10 13:05:15 +0200 | [diff] [blame] | 135 | |
| 136 | def _add_show_cover_comments(parser): |
| 137 | parser.add_argument('-C', '--show-cover-comments', action='store_true', |
| 138 | help='Show comments from the cover letter') |
| 139 | |
Simon Glass | e9f14cd | 2025-05-10 13:05:08 +0200 | [diff] [blame] | 140 | |
Simon Glass | be7e6c0 | 2025-05-10 13:05:12 +0200 | [diff] [blame] | 141 | def add_patchwork_subparser(subparsers): |
| 142 | """Add the 'patchwork' subparser |
| 143 | |
| 144 | Args: |
| 145 | subparsers (argparse action): Subparser parent |
| 146 | |
| 147 | Return: |
| 148 | ArgumentParser: patchwork subparser |
| 149 | """ |
| 150 | patchwork = subparsers.add_parser( |
Simon Glass | 55d335f | 2025-05-10 13:05:13 +0200 | [diff] [blame] | 151 | 'patchwork', aliases=ALIASES['patchwork'], |
Simon Glass | be7e6c0 | 2025-05-10 13:05:12 +0200 | [diff] [blame] | 152 | help='Manage patchwork connection') |
| 153 | patchwork.defaults_cmds = [ |
| 154 | ['set-project', 'U-Boot'], |
| 155 | ] |
| 156 | patchwork_subparsers = patchwork.add_subparsers(dest='subcmd') |
| 157 | patchwork_subparsers.add_parser('get-project') |
| 158 | uset = patchwork_subparsers.add_parser('set-project') |
| 159 | uset.add_argument( |
| 160 | 'project_name', help="Patchwork project name, e.g. 'U-Boot'") |
| 161 | return patchwork |
| 162 | |
Simon Glass | 5e52d17 | 2025-05-10 13:05:15 +0200 | [diff] [blame] | 163 | |
| 164 | def add_series_subparser(subparsers): |
| 165 | """Add the 'series' subparser |
| 166 | |
| 167 | Args: |
| 168 | subparsers (argparse action): Subparser parent |
| 169 | |
| 170 | Return: |
| 171 | ArgumentParser: series subparser |
| 172 | """ |
| 173 | def _add_allow_unmarked(parser): |
| 174 | parser.add_argument('-M', '--allow-unmarked', action='store_true', |
| 175 | default=False, |
| 176 | help="Don't require commits to be marked") |
| 177 | |
| 178 | def _add_mark(parser): |
| 179 | parser.add_argument( |
| 180 | '-m', '--mark', action='store_true', |
| 181 | help='Mark unmarked commits with a Change-Id field') |
| 182 | |
| 183 | def _add_update(parser): |
| 184 | parser.add_argument('-u', '--update', action='store_true', |
| 185 | help='Update the branch commit') |
| 186 | |
| 187 | def _add_wait(parser, default_s): |
| 188 | """Add a -w option to a parser |
| 189 | |
| 190 | Args: |
| 191 | parser (ArgumentParser): Parser to adjust |
| 192 | default_s (int): Default value to use, in seconds |
| 193 | """ |
| 194 | parser.add_argument( |
| 195 | '-w', '--autolink-wait', type=int, default=default_s, |
| 196 | help='Seconds to wait for patchwork to get a sent series') |
| 197 | |
| 198 | def _upstream_add(parser): |
| 199 | parser.add_argument('-U', '--upstream', help='Commit to end before') |
| 200 | |
| 201 | def _add_gather(parser): |
| 202 | parser.add_argument( |
| 203 | '-G', '--no-gather-tags', dest='gather_tags', default=True, |
| 204 | action='store_false', |
| 205 | help="Don't gather review/test tags / update local series") |
| 206 | |
| 207 | series = subparsers.add_parser('series', aliases=ALIASES['series'], |
| 208 | help='Manage series of patches') |
| 209 | series.defaults_cmds = [ |
| 210 | ['set-link', 'fred'], |
| 211 | ] |
| 212 | series.add_argument( |
| 213 | '-n', '--dry-run', action='store_true', dest='dry_run', default=False, |
| 214 | help="Do a dry run (create but don't email patches)") |
| 215 | series.add_argument('-s', '--series', help='Name of series') |
| 216 | series.add_argument('-V', '--version', type=int, |
| 217 | help='Version number to link') |
| 218 | series_subparsers = series.add_subparsers(dest='subcmd') |
| 219 | |
| 220 | # This causes problem at present, perhaps due to the 'defaults' handling in |
| 221 | # settings |
| 222 | # series_subparsers.required = True |
| 223 | |
| 224 | add = series_subparsers.add_parser('add') |
| 225 | add.add_argument('-D', '--desc', |
| 226 | help='Series description / cover-letter title') |
| 227 | add.add_argument( |
| 228 | '-f', '--force-version', action='store_true', |
| 229 | help='Change the Series-version on a series to match its branch') |
| 230 | _add_mark(add) |
| 231 | _add_allow_unmarked(add) |
| 232 | _upstream_add(add) |
| 233 | |
| 234 | series_subparsers.add_parser('archive', aliases=ALIASES['archive']) |
| 235 | |
| 236 | auto = series_subparsers.add_parser('autolink', |
| 237 | aliases=ALIASES['autolink']) |
| 238 | _add_update(auto) |
| 239 | _add_wait(auto, 0) |
| 240 | |
| 241 | aall = series_subparsers.add_parser('autolink-all') |
| 242 | aall.add_argument('-a', '--link-all-versions', action='store_true', |
| 243 | help='Link all series versions, not just the latest') |
| 244 | aall.add_argument('-r', '--replace-existing', action='store_true', |
| 245 | help='Replace existing links') |
| 246 | _add_update(aall) |
| 247 | |
| 248 | series_subparsers.add_parser('dec') |
| 249 | |
| 250 | gat = series_subparsers.add_parser('gather', aliases=ALIASES['gather']) |
| 251 | _add_gather(gat) |
| 252 | _add_show_comments(gat) |
| 253 | _add_show_cover_comments(gat) |
| 254 | |
| 255 | sall = series_subparsers.add_parser('gather-all') |
| 256 | sall.add_argument( |
| 257 | '-a', '--gather-all-versions', action='store_true', |
| 258 | help='Gather tags from all series versions, not just the latest') |
| 259 | _add_gather(sall) |
| 260 | _add_show_comments(sall) |
| 261 | _add_show_cover_comments(sall) |
| 262 | |
| 263 | series_subparsers.add_parser('get-link') |
| 264 | series_subparsers.add_parser('inc') |
| 265 | series_subparsers.add_parser('ls') |
| 266 | |
| 267 | mar = series_subparsers.add_parser('mark') |
| 268 | mar.add_argument('-m', '--allow-marked', action='store_true', |
| 269 | default=False, |
| 270 | help="Don't require commits to be unmarked") |
| 271 | |
| 272 | series_subparsers.add_parser('open', aliases=ALIASES['open']) |
| 273 | pat = series_subparsers.add_parser( |
| 274 | 'patches', epilog='Show a list of patches and optional details') |
| 275 | pat.add_argument('-t', '--commit', action='store_true', |
| 276 | help='Show the commit and diffstat') |
| 277 | pat.add_argument('-p', '--patch', action='store_true', |
| 278 | help='Show the patch body') |
| 279 | |
| 280 | prog = series_subparsers.add_parser('progress', |
| 281 | aliases=ALIASES['progress']) |
| 282 | prog.add_argument('-a', '--show-all-versions', action='store_true', |
| 283 | help='Show all series versions, not just the latest') |
| 284 | prog.add_argument('-l', '--list-patches', action='store_true', |
| 285 | help='List patch subject and status') |
| 286 | |
| 287 | ren = series_subparsers.add_parser('rename') |
| 288 | ren.add_argument('-N', '--new-name', help='New name for the series') |
| 289 | |
| 290 | series_subparsers.add_parser('rm') |
| 291 | series_subparsers.add_parser('rm-version', aliases=ALIASES['rm-version']) |
| 292 | |
| 293 | scan = series_subparsers.add_parser('scan') |
| 294 | _add_mark(scan) |
| 295 | _add_allow_unmarked(scan) |
| 296 | _upstream_add(scan) |
| 297 | |
| 298 | ssend = series_subparsers.add_parser('send') |
| 299 | add_send_args(ssend) |
| 300 | ssend.add_argument( |
| 301 | '--no-autolink', action='store_false', default=True, dest='autolink', |
| 302 | help='Monitor patchwork after sending so the series can be autolinked') |
| 303 | _add_wait(ssend, 120) |
| 304 | |
| 305 | setl = series_subparsers.add_parser('set-link') |
| 306 | _add_update(setl) |
| 307 | |
| 308 | setl.add_argument( |
| 309 | 'link', help='Link to use, i.e. patchwork series number (e.g. 452329)') |
| 310 | stat = series_subparsers.add_parser('status', aliases=ALIASES['status']) |
| 311 | _add_show_comments(stat) |
| 312 | _add_show_cover_comments(stat) |
| 313 | |
| 314 | series_subparsers.add_parser('summary') |
| 315 | |
| 316 | series_subparsers.add_parser('unarchive', aliases=ALIASES['unarchive']) |
| 317 | |
| 318 | unm = series_subparsers.add_parser('unmark') |
| 319 | _add_allow_unmarked(unm) |
| 320 | |
| 321 | ver = series_subparsers.add_parser( |
| 322 | 'version-change', help='Change a version to a different version') |
| 323 | ver.add_argument('--new-version', type=int, |
| 324 | help='New version number to change this one too') |
| 325 | |
| 326 | return series |
| 327 | |
Simon Glass | be7e6c0 | 2025-05-10 13:05:12 +0200 | [diff] [blame] | 328 | |
Simon Glass | 44ecdd4 | 2025-05-08 06:21:33 +0200 | [diff] [blame] | 329 | def add_send_subparser(subparsers): |
| 330 | """Add the 'send' subparser |
| 331 | |
| 332 | Args: |
| 333 | subparsers (argparse action): Subparser parent |
| 334 | |
| 335 | Return: |
| 336 | ArgumentParser: send subparser |
| 337 | """ |
| 338 | send = subparsers.add_parser( |
| 339 | 'send', help='Format, check and email patches (default command)') |
| 340 | send.add_argument( |
| 341 | '-b', '--branch', type=str, |
| 342 | help="Branch to process (by default, the current branch)") |
| 343 | send.add_argument( |
| 344 | '-n', '--dry-run', action='store_true', dest='dry_run', |
| 345 | default=False, help="Do a dry run (create but don't email patches)") |
| 346 | send.add_argument( |
| 347 | '--cc-cmd', dest='cc_cmd', type=str, action='store', |
| 348 | default=None, help='Output cc list for patch file (used by git)') |
| 349 | add_send_args(send) |
Simon Glass | 22ce641 | 2023-11-04 10:25:20 -0600 | [diff] [blame] | 350 | send.add_argument('patchfiles', nargs='*') |
Simon Glass | e55a11c | 2025-05-08 06:09:46 +0200 | [diff] [blame] | 351 | return send |
Simon Glass | 22ce641 | 2023-11-04 10:25:20 -0600 | [diff] [blame] | 352 | |
Simon Glass | e55a11c | 2025-05-08 06:09:46 +0200 | [diff] [blame] | 353 | |
| 354 | def add_status_subparser(subparsers): |
| 355 | """Add the 'status' subparser |
| 356 | |
| 357 | Args: |
| 358 | subparsers (argparse action): Subparser parent |
Simon Glass | 22ce641 | 2023-11-04 10:25:20 -0600 | [diff] [blame] | 359 | |
Simon Glass | e55a11c | 2025-05-08 06:09:46 +0200 | [diff] [blame] | 360 | Return: |
| 361 | ArgumentParser: status subparser |
| 362 | """ |
Simon Glass | 55d335f | 2025-05-10 13:05:13 +0200 | [diff] [blame] | 363 | status = subparsers.add_parser('status', aliases=ALIASES['status'], |
Simon Glass | 22ce641 | 2023-11-04 10:25:20 -0600 | [diff] [blame] | 364 | help='Check status of patches in patchwork') |
Simon Glass | e9f14cd | 2025-05-10 13:05:08 +0200 | [diff] [blame] | 365 | _add_show_comments(status) |
Simon Glass | 22ce641 | 2023-11-04 10:25:20 -0600 | [diff] [blame] | 366 | status.add_argument( |
| 367 | '-d', '--dest-branch', type=str, |
| 368 | help='Name of branch to create with collected responses') |
| 369 | status.add_argument('-f', '--force', action='store_true', |
| 370 | help='Force overwriting an existing branch') |
Simon Glass | 802eeea | 2025-04-29 07:22:25 -0600 | [diff] [blame] | 371 | status.add_argument('-T', '--single-thread', action='store_true', |
| 372 | help='Disable multithreading when reading patchwork') |
Simon Glass | e55a11c | 2025-05-08 06:09:46 +0200 | [diff] [blame] | 373 | return status |
| 374 | |
| 375 | |
Simon Glass | 14bac32 | 2025-05-10 13:05:16 +0200 | [diff] [blame] | 376 | def add_upstream_subparser(subparsers): |
| 377 | """Add the 'status' subparser |
| 378 | |
| 379 | Args: |
| 380 | subparsers (argparse action): Subparser parent |
| 381 | |
| 382 | Return: |
| 383 | ArgumentParser: status subparser |
| 384 | """ |
| 385 | upstream = subparsers.add_parser('upstream', aliases=ALIASES['upstream'], |
| 386 | help='Manage upstream destinations') |
| 387 | upstream.defaults_cmds = [ |
| 388 | ['add', 'us', 'http://fred'], |
| 389 | ['delete', 'us'], |
| 390 | ] |
| 391 | upstream_subparsers = upstream.add_subparsers(dest='subcmd') |
| 392 | uadd = upstream_subparsers.add_parser('add') |
| 393 | uadd.add_argument('remote_name', |
| 394 | help="Git remote name used for this upstream, e.g. 'us'") |
| 395 | uadd.add_argument( |
| 396 | 'url', help='URL to use for this upstream, e.g. ' |
| 397 | "'https://gitlab.denx.de/u-boot/u-boot.git'") |
| 398 | udel = upstream_subparsers.add_parser('delete') |
| 399 | udel.add_argument( |
| 400 | 'remote_name', |
| 401 | help="Git remote name used for this upstream, e.g. 'us'") |
| 402 | upstream_subparsers.add_parser('list') |
| 403 | udef = upstream_subparsers.add_parser('default') |
| 404 | udef.add_argument('-u', '--unset', action='store_true', |
| 405 | help='Unset the default upstream') |
| 406 | udef.add_argument('remote_name', nargs='?', |
| 407 | help="Git remote name used for this upstream, e.g. 'us'") |
| 408 | return upstream |
| 409 | |
| 410 | |
Simon Glass | e55a11c | 2025-05-08 06:09:46 +0200 | [diff] [blame] | 411 | def setup_parser(): |
| 412 | """Set up command-line parser |
| 413 | |
| 414 | Returns: |
| 415 | argparse.Parser object |
| 416 | """ |
| 417 | epilog = '''Create patches from commits in a branch, check them and email |
| 418 | them as specified by tags you place in the commits. Use -n to do a dry |
| 419 | run first.''' |
| 420 | |
Simon Glass | d9ec747 | 2025-05-10 13:05:07 +0200 | [diff] [blame] | 421 | parser = ErrorCatchingArgumentParser(epilog=epilog) |
Simon Glass | 44ecdd4 | 2025-05-08 06:21:33 +0200 | [diff] [blame] | 422 | parser.add_argument( |
| 423 | '-D', '--debug', action='store_true', |
Simon Glass | e55a11c | 2025-05-08 06:09:46 +0200 | [diff] [blame] | 424 | help='Enabling debugging (provides a full traceback on error)') |
| 425 | parser.add_argument( |
| 426 | '-N', '--no-capture', action='store_true', |
| 427 | help='Disable capturing of console output in tests') |
| 428 | parser.add_argument('-p', '--project', default=project.detect_project(), |
| 429 | help="Project name; affects default option values and " |
| 430 | "aliases [default: %(default)s]") |
| 431 | parser.add_argument('-P', '--patchwork-url', |
| 432 | default='https://patchwork.ozlabs.org', |
| 433 | help='URL of patchwork server [default: %(default)s]') |
Simon Glass | 44ecdd4 | 2025-05-08 06:21:33 +0200 | [diff] [blame] | 434 | parser.add_argument( |
| 435 | '-T', '--thread', action='store_true', dest='thread', |
| 436 | default=False, help='Create patches as a single thread') |
Simon Glass | e55a11c | 2025-05-08 06:09:46 +0200 | [diff] [blame] | 437 | parser.add_argument( |
| 438 | '-v', '--verbose', action='store_true', dest='verbose', default=False, |
| 439 | help='Verbose output of errors and warnings') |
| 440 | parser.add_argument( |
| 441 | '-X', '--test-preserve-dirs', action='store_true', |
| 442 | help='Preserve and display test-created directories') |
| 443 | parser.add_argument( |
| 444 | '-H', '--full-help', action='store_true', dest='full_help', |
| 445 | default=False, help='Display the README file') |
| 446 | |
| 447 | subparsers = parser.add_subparsers(dest='cmd') |
| 448 | add_send_subparser(subparsers) |
Simon Glass | be7e6c0 | 2025-05-10 13:05:12 +0200 | [diff] [blame] | 449 | patchwork = add_patchwork_subparser(subparsers) |
Simon Glass | 5e52d17 | 2025-05-10 13:05:15 +0200 | [diff] [blame] | 450 | series = add_series_subparser(subparsers) |
Simon Glass | e55a11c | 2025-05-08 06:09:46 +0200 | [diff] [blame] | 451 | add_status_subparser(subparsers) |
Simon Glass | 14bac32 | 2025-05-10 13:05:16 +0200 | [diff] [blame] | 452 | upstream = add_upstream_subparser(subparsers) |
Simon Glass | e55a11c | 2025-05-08 06:09:46 +0200 | [diff] [blame] | 453 | |
| 454 | # Only add the 'test' action if the test data files are available. |
| 455 | if HAS_TESTS: |
| 456 | test_parser = subparsers.add_parser('test', help='Run tests') |
| 457 | test_parser.add_argument('testname', type=str, default=None, nargs='?', |
| 458 | help="Specify the test to run") |
| 459 | |
Simon Glass | 65a4579 | 2025-05-10 13:05:09 +0200 | [diff] [blame] | 460 | parsers = { |
| 461 | 'main': parser, |
Simon Glass | 5e52d17 | 2025-05-10 13:05:15 +0200 | [diff] [blame] | 462 | 'series': series, |
Simon Glass | be7e6c0 | 2025-05-10 13:05:12 +0200 | [diff] [blame] | 463 | 'patchwork': patchwork, |
Simon Glass | 14bac32 | 2025-05-10 13:05:16 +0200 | [diff] [blame] | 464 | 'upstream': upstream, |
Simon Glass | 65a4579 | 2025-05-10 13:05:09 +0200 | [diff] [blame] | 465 | } |
| 466 | return parsers |
Simon Glass | 8b521cf | 2025-05-08 05:58:10 +0200 | [diff] [blame] | 467 | |
| 468 | |
Simon Glass | 65a4579 | 2025-05-10 13:05:09 +0200 | [diff] [blame] | 469 | def parse_args(argv=None, config_fname=None, parsers=None): |
Simon Glass | 8b521cf | 2025-05-08 05:58:10 +0200 | [diff] [blame] | 470 | """Parse command line arguments from sys.argv[] |
| 471 | |
| 472 | Args: |
| 473 | argv (str or None): Arguments to process, or None to use sys.argv[1:] |
| 474 | config_fname (str): Config file to read, or None for default, or False |
| 475 | for an empty config |
| 476 | |
| 477 | Returns: |
| 478 | tuple containing: |
| 479 | options: command line options |
| 480 | args: command lin arguments |
| 481 | """ |
Simon Glass | 65a4579 | 2025-05-10 13:05:09 +0200 | [diff] [blame] | 482 | if not parsers: |
| 483 | parsers = setup_parser() |
| 484 | parser = parsers['main'] |
Simon Glass | 22ce641 | 2023-11-04 10:25:20 -0600 | [diff] [blame] | 485 | |
| 486 | # Parse options twice: first to get the project and second to handle |
| 487 | # defaults properly (which depends on project) |
| 488 | # Use parse_known_args() in case 'cmd' is omitted |
Simon Glass | 8b521cf | 2025-05-08 05:58:10 +0200 | [diff] [blame] | 489 | if not argv: |
| 490 | argv = sys.argv[1:] |
| 491 | |
Simon Glass | 22ce641 | 2023-11-04 10:25:20 -0600 | [diff] [blame] | 492 | args, rest = parser.parse_known_args(argv) |
| 493 | if hasattr(args, 'project'): |
Simon Glass | 8b521cf | 2025-05-08 05:58:10 +0200 | [diff] [blame] | 494 | settings.Setup(parser, args.project, argv, config_fname) |
Simon Glass | 22ce641 | 2023-11-04 10:25:20 -0600 | [diff] [blame] | 495 | args, rest = parser.parse_known_args(argv) |
| 496 | |
| 497 | # If we have a command, it is safe to parse all arguments |
| 498 | if args.cmd: |
| 499 | args = parser.parse_args(argv) |
Simon Glass | b8a14f9 | 2025-05-08 06:30:14 +0200 | [diff] [blame] | 500 | elif not args.full_help: |
Simon Glass | 22ce641 | 2023-11-04 10:25:20 -0600 | [diff] [blame] | 501 | # No command, so insert it after the known arguments and before the ones |
| 502 | # that presumably relate to the 'send' subcommand |
| 503 | nargs = len(rest) |
| 504 | argv = argv[:-nargs] + ['send'] + rest |
| 505 | args = parser.parse_args(argv) |
| 506 | |
Simon Glass | 55d335f | 2025-05-10 13:05:13 +0200 | [diff] [blame] | 507 | # Resolve aliases |
| 508 | for full, aliases in ALIASES.items(): |
| 509 | if args.cmd in aliases: |
| 510 | args.cmd = full |
| 511 | if 'subcmd' in args and args.subcmd in aliases: |
| 512 | args.subcmd = full |
| 513 | if args.cmd in ['series', 'upstream', 'patchwork'] and not args.subcmd: |
| 514 | parser.parse_args([args.cmd, '--help']) |
| 515 | |
Simon Glass | 22ce641 | 2023-11-04 10:25:20 -0600 | [diff] [blame] | 516 | return args |