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