patman correct import of u_boot_pylib

correct long-standing EFI framebuffer bug
 minor test refactor
 -----BEGIN PGP SIGNATURE-----
 
 iQFFBAABCgAvFiEEslwAIq+Gp8wWVbYnfxc6PpAIreYFAmVUxF0RHHNqZ0BjaHJv
 bWl1bS5vcmcACgkQfxc6PpAIreandwgAkAhSYyADGMtGzbGbWK1t4EvYryaRCKvk
 KwbAWjbAfK3xOr8lUviRDDNEn2T1UQKYLPf64h6e1ivMrpS1M9JW8N0dEttagk1R
 O4KLMFkbBZXbbVA/ovhBvEDSBVWCnj0bRSq5nKnoCRCZAtQJnRpQXAZlUz9WT+js
 zJqgVoNua44akYtqulpgX4O6/8SvecQBJoP0yIyQCbXSNSOr7zcADIxq9yJJ0KrC
 4nFQ/qCvLsfrfeRX/NoSbEftX480fjyzpTFsp9XqvJPvtFQbzDUSaXSQ5QSUWNNl
 DrjNjUElqJv7cGSj73mm6yarVyRDygNyu49T/vgAvq3LwgNwdLuUqA==
 =tbfh
 -----END PGP SIGNATURE-----

Merge tag 'dm-pull-15nov23' of https://source.denx.de/u-boot/custodians/u-boot-dm

patman correct import of u_boot_pylib
correct long-standing EFI framebuffer bug
minor test refactor
This commit is contained in:
Tom Rini 2023-11-15 14:15:21 -05:00
commit 169c3cc49e
12 changed files with 402 additions and 208 deletions

View file

@ -219,7 +219,7 @@ int os_map_file(const char *pathname, int os_flags, void **bufp, int *sizep)
{
void *ptr;
off_t size;
int ifd;
int ifd, ret = 0;
ifd = os_open(pathname, os_flags);
if (ifd < 0) {
@ -229,23 +229,28 @@ int os_map_file(const char *pathname, int os_flags, void **bufp, int *sizep)
size = os_filesize(ifd);
if (size < 0) {
printf("Cannot get file size of '%s'\n", pathname);
return -EIO;
ret = -EIO;
goto out;
}
if ((unsigned long long)size > (unsigned long long)SIZE_MAX) {
printf("File '%s' too large to map\n", pathname);
return -EIO;
ret = -EIO;
goto out;
}
ptr = mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, ifd, 0);
if (ptr == MAP_FAILED) {
printf("Can't map file '%s': %s\n", pathname, strerror(errno));
return -EPERM;
ret = -EPERM;
goto out;
}
*bufp = ptr;
*sizep = size;
return 0;
out:
os_close(ifd);
return ret;
}
int os_unmap(void *buf, int size)

View file

@ -190,10 +190,12 @@ int expo_render(struct expo *exp)
struct udevice *dev = exp->display;
struct video_priv *vid_priv = dev_get_uclass_priv(dev);
struct scene *scn = NULL;
enum colour_idx back;
u32 colour;
int ret;
colour = video_index_to_colour(vid_priv, VID_WHITE);
back = CONFIG_IS_ENABLED(SYS_WHITE_ON_BLACK) ? VID_BLACK : VID_WHITE;
colour = video_index_to_colour(vid_priv, back);
ret = video_fill(dev, colour);
if (ret)
return log_msg_ret("fill", ret);

View file

@ -89,6 +89,44 @@ static void show_footer(int count, int num_valid)
num_valid);
}
/**
* bootflow_handle_menu() - Handle running the menu and updating cur bootflow
*
* This shows the menu, allows the user to select something and then prints
* what happened
*
* @std: bootstd information
* @text_mode: true to run the menu in text mode
* @bflowp: Returns selected bootflow, on success
* Return: 0 on success (a bootflow was selected), -EAGAIN if nothing was
* chosen, other -ve value on other error
*/
__maybe_unused static int bootflow_handle_menu(struct bootstd_priv *std,
bool text_mode,
struct bootflow **bflowp)
{
struct bootflow *bflow;
int ret;
ret = bootflow_menu_run(std, text_mode, &bflow);
if (ret) {
if (ret == -EAGAIN) {
printf("Nothing chosen\n");
std->cur_bootflow = NULL;
} else {
printf("Menu failed (err=%d)\n", ret);
}
return ret;
}
printf("Selected: %s\n", bflow->os_name ? bflow->os_name : bflow->name);
std->cur_bootflow = bflow;
*bflowp = bflow;
return 0;
}
static int do_bootflow_scan(struct cmd_tbl *cmdtp, int flag, int argc,
char *const argv[])
{
@ -455,18 +493,9 @@ static int do_bootflow_menu(struct cmd_tbl *cmdtp, int flag, int argc,
if (ret)
return CMD_RET_FAILURE;
ret = bootflow_menu_run(std, text_mode, &bflow);
if (ret) {
if (ret == -EAGAIN)
printf("Nothing chosen\n");
else {
printf("Menu failed (err=%d)\n", ret);
return CMD_RET_FAILURE;
}
}
printf("Selected: %s\n", bflow->os_name ? bflow->os_name : bflow->name);
std->cur_bootflow = bflow;
ret = bootflow_handle_menu(std, text_mode, &bflow);
if (ret)
return CMD_RET_FAILURE;
return 0;
}

View file

@ -137,7 +137,7 @@ ulong bootstage_add_record(enum bootstage_id id, const char *name,
rec->flags = flags;
rec->id = id;
} else {
log_warning("Bootstage space exhasuted\n");
log_warning("Bootstage space exhausted\n");
}
}

View file

@ -15,6 +15,7 @@ Synopis
bootflow read
bootflow boot
bootflow cmdline [set|get|clear|delete|auto] <param> [<value>]
bootfloe menu [-t]
Description
-----------
@ -24,6 +25,9 @@ locate bootflows, list them and boot them.
See :doc:`../../develop/bootstd` for more information.
Note that `CONFIG_BOOTSTD_FULL` (which enables `CONFIG_CMD_BOOTFLOW_FULL) must
be enabled to obtain full functionality with this command. Otherwise, it only
supports `bootflow scan` which scans and boots the first available bootflow.
bootflow scan
~~~~~~~~~~~~~
@ -247,6 +251,16 @@ can be used to set the early console (or console) to a suitable value so that
output appears on the serial port. This is only supported by the 16550 serial
driver so far.
bootflow menu
~~~~~~~~~~~~~
This shows a menu with available bootflows. The user can select a particular
bootflow, which then becomes the current one.
The `-t` flag requests a text menu. Otherwise, if a display is available, a
graphical menu is shown.
Example
-------
@ -658,6 +672,56 @@ Now the buffer can be accessed::
77b7e4e0: 320fc000 08e8ba0f c031300f b8d0000f ...2.....01.....
77b7e4f0: 00000020 6ad8000f 00858d10 50000002 ......j.......P
This shows using a text menu to boot an OS::
=> bootflow scan
=> bootfl list
=> bootfl menu -t
U-Boot : Boot Menu
UP and DOWN to choose, ENTER to select
> 0 mmc1 mmc1.bootdev.whole
1 mmc1 Fedora-Workstation-armhfp-31-1.9 (5.3.7-301.fc31.armv7hl)
2 mmc1 mmc1.bootdev.part_1
3 mmc4 mmc4.bootdev.whole
4 mmc4 Armbian
5 mmc4 mmc4.bootdev.part_1
6 mmc5 mmc5.bootdev.whole
7 mmc5 ChromeOS
8 mmc5 ChromeOS
U-Boot : Boot Menu
UP and DOWN to choose, ENTER to select
0 mmc1 mmc1.bootdev.whole
> 1 mmc1 Fedora-Workstation-armhfp-31-1.9 (5.3.7-301.fc31.armv7hl)
2 mmc1 mmc1.bootdev.part_1
3 mmc4 mmc4.bootdev.whole
4 mmc4 Armbian
5 mmc4 mmc4.bootdev.part_1
6 mmc5 mmc5.bootdev.whole
7 mmc5 ChromeOS
8 mmc5 ChromeOS
U-Boot : Boot Menu
Selected: Fedora-Workstation-armhfp-31-1.9 (5.3.7-301.fc31.armv7hl)
=> bootfl boot
** Booting bootflow 'mmc1.bootdev.part_1' with extlinux
Ignoring unknown command: ui
Ignoring malformed menu command: autoboot
Ignoring malformed menu command: hidden
Ignoring unknown command: totaltimeout
Fedora-Workstation-armhfp-31-1.9 Boot Options.
1: Fedora-Workstation-armhfp-31-1.9 (5.3.7-301.fc31.armv7hl)
Enter choice: 1
1: Fedora-Workstation-armhfp-31-1.9 (5.3.7-301.fc31.armv7hl)
Retrieving file: /vmlinuz-5.3.7-301.fc31.armv7hl
Retrieving file: /initramfs-5.3.7-301.fc31.armv7hl.img
append: ro root=UUID=9732b35b-4cd5-458b-9b91-80f7047e0b8a rhgb quiet LANG=en_US.UTF-8 cma=192MB cma=256MB
Retrieving file: /dtb-5.3.7-301.fc31.armv7hl/sandbox.dtb
...
Return value
------------
@ -667,6 +731,9 @@ return to U-Boot. If something about the U-Boot processing fails, then the
return value $? is 1. If the boot succeeds but for some reason the Operating
System returns, then $? is 0, indicating success.
For `bootflow menu` the return value is $? is 0 (true) if an option was choses,
else 1.
For other subcommands, the return value $? is always 0 (true).

View file

@ -48,7 +48,6 @@ config VPL_DM
config DM_WARN
bool "Enable warnings in driver model"
depends on DM
default y
help
Enable this to see warnings related to driver model.

View file

@ -11,9 +11,7 @@ struct dm_stats;
#if CONFIG_IS_ENABLED(DM_WARN)
#define dm_warn(fmt...) log(LOGC_DM, LOGL_WARNING, ##fmt)
#else
static inline void dm_warn(const char *fmt, ...)
{
}
#define dm_warn(fmt...) log(LOGC_DM, LOGL_DEBUG, ##fmt)
#endif
struct list_head;

View file

@ -21,9 +21,12 @@ struct udevice;
* @align: Frame-buffer alignment, indicating the memory boundary the frame
* buffer should start on. If 0, 1MB is assumed
* @size: Frame-buffer size, in bytes
* @base: Base address of frame buffer, 0 if not yet known
* @copy_base: Base address of a hardware copy of the frame buffer. See
* CONFIG_VIDEO_COPY.
* @base: Base address of frame buffer, 0 if not yet known. If CONFIG_VIDEO_COPY
* is enabled, this is the software copy, so writes to this will not be
* visible until vidconsole_sync_copy() is called. If CONFIG_VIDEO_COPY is
* disabled, this is the hardware framebuffer.
* @copy_base: Base address of a hardware copy of the frame buffer. If
* CONFIG_VIDEO_COPY is disabled, this is not used.
* @copy_size: Size of copy framebuffer, used if @size is 0
* @hide_logo: Hide the logo (used for testing)
*/

View file

@ -10,6 +10,7 @@
#include <efi_loader.h>
#include <log.h>
#include <malloc.h>
#include <mapmem.h>
#include <video.h>
#include <asm/global_data.h>
@ -467,10 +468,10 @@ efi_status_t efi_gop_register(void)
struct efi_gop_obj *gopobj;
u32 bpix, format, col, row;
u64 fb_base, fb_size;
void *fb;
efi_status_t ret;
struct udevice *vdev;
struct video_priv *priv;
struct video_uc_plat *plat;
/* We only support a single video output device for now */
if (uclass_first_device_err(UCLASS_VIDEO, &vdev)) {
@ -483,9 +484,10 @@ efi_status_t efi_gop_register(void)
format = priv->format;
col = video_get_xsize(vdev);
row = video_get_ysize(vdev);
fb_base = (uintptr_t)priv->fb;
fb_size = priv->fb_size;
fb = priv->fb;
plat = dev_get_uclass_plat(vdev);
fb_base = IS_ENABLED(CONFIG_VIDEO_COPY) ? plat->copy_base : plat->base;
fb_size = plat->size;
switch (bpix) {
case VIDEO_BPP16:
@ -547,7 +549,7 @@ efi_status_t efi_gop_register(void)
}
gopobj->info.pixels_per_scanline = col;
gopobj->bpix = bpix;
gopobj->fb = fb;
gopobj->fb = map_sysmem(fb_base, fb_size);
return EFI_SUCCESS;
}

View file

@ -511,19 +511,27 @@ BOOTSTD_TEST(bootflow_cmd_boot, UT_TESTF_DM | UT_TESTF_SCAN_FDT);
/**
* prep_mmc_bootdev() - Set up an mmc bootdev so we can access other distros
*
* After calling this function, set std->bootdev_order to *@old_orderp to
* restore normal operation of bootstd (i.e. with the original bootdev order)
*
* @uts: Unit test state
* @mmc_dev: MMC device to use, e.g. "mmc4"
* @mmc_dev: MMC device to use, e.g. "mmc4". Note that this must remain valid
* in the caller until
* @bind_cros: true to bind the ChromiumOS bootmeth
* @old_orderp: Returns the original bootdev order, which must be restored
* Returns 0 on success, -ve on failure
*/
static int prep_mmc_bootdev(struct unit_test_state *uts, const char *mmc_dev,
bool bind_cros)
bool bind_cros, const char ***old_orderp)
{
const char *order[] = {"mmc2", "mmc1", mmc_dev, NULL};
static const char *order[] = {"mmc2", "mmc1", NULL, NULL};
struct udevice *dev, *bootstd;
struct bootstd_priv *std;
const char **old_order;
ofnode root, node;
order[2] = mmc_dev;
/* Enable the mmc4 node since we need a second bootflow */
root = oftree_root(oftree_default());
node = ofnode_find_subnode(root, mmc_dev);
@ -546,26 +554,49 @@ static int prep_mmc_bootdev(struct unit_test_state *uts, const char *mmc_dev,
std = dev_get_priv(bootstd);
old_order = std->bootdev_order;
std->bootdev_order = order;
*old_orderp = old_order;
return 0;
}
/**
* scan_mmc_bootdev() - Set up an mmc bootdev so we can access other distros
*
* @uts: Unit test state
* @mmc_dev: MMC device to use, e.g. "mmc4"
* @bind_cros: true to bind the ChromiumOS bootmeth
* Returns 0 on success, -ve on failure
*/
static int scan_mmc_bootdev(struct unit_test_state *uts, const char *mmc_dev,
bool bind_cros)
{
struct bootstd_priv *std;
struct udevice *bootstd;
const char **old_order;
ut_assertok(prep_mmc_bootdev(uts, mmc_dev, bind_cros, &old_order));
console_record_reset_enable();
ut_assertok(run_command("bootflow scan", 0));
ut_assert_console_end();
/* Restore the order used by the device tree */
ut_assertok(uclass_first_device_err(UCLASS_BOOTSTD, &bootstd));
std = dev_get_priv(bootstd);
std->bootdev_order = old_order;
return 0;
}
/**
* prep_mmc4_bootdev() - Set up the mmc4 bootdev so we can access a fake Armbian
* scan_mmc4_bootdev() - Set up the mmc4 bootdev so we can access a fake Armbian
*
* @uts: Unit test state
* Returns 0 on success, -ve on failure
*/
static int prep_mmc4_bootdev(struct unit_test_state *uts)
static int scan_mmc4_bootdev(struct unit_test_state *uts)
{
ut_assertok(prep_mmc_bootdev(uts, "mmc4", false));
ut_assertok(scan_mmc_bootdev(uts, "mmc4", false));
return 0;
}
@ -573,9 +604,13 @@ static int prep_mmc4_bootdev(struct unit_test_state *uts)
/* Check 'bootflow menu' to select a bootflow */
static int bootflow_cmd_menu(struct unit_test_state *uts)
{
struct bootstd_priv *std;
char prev[3];
ut_assertok(prep_mmc4_bootdev(uts));
/* get access to the current bootflow */
ut_assertok(bootstd_get_priv(&std));
ut_assertok(scan_mmc4_bootdev(uts));
/* Add keypresses to move to and select the second one in the list */
prev[0] = CTL_CH('n');
@ -585,6 +620,17 @@ static int bootflow_cmd_menu(struct unit_test_state *uts)
ut_assertok(run_command("bootflow menu", 0));
ut_assert_nextline("Selected: Armbian");
ut_assertnonnull(std->cur_bootflow);
ut_assert_console_end();
/* Check not selecting anything */
prev[0] = '\e';
prev[1] = '\0';
ut_asserteq(1, console_in_puts(prev));
ut_asserteq(1, run_command("bootflow menu", 0));
ut_assertnull(std->cur_bootflow);
ut_assert_nextline("Nothing chosen");
ut_assert_console_end();
return 0;
@ -681,7 +727,7 @@ static int bootflow_menu_theme(struct unit_test_state *uts)
ofnode node;
int i;
ut_assertok(prep_mmc4_bootdev(uts));
ut_assertok(scan_mmc4_bootdev(uts));
ut_assertok(bootflow_menu_new(&exp));
node = ofnode_path("/bootstd/theme");
@ -996,7 +1042,7 @@ BOOTSTD_TEST(bootflow_cmdline_special, 0);
/* Test ChromiumOS bootmeth */
static int bootflow_cros(struct unit_test_state *uts)
{
ut_assertok(prep_mmc_bootdev(uts, "mmc5", true));
ut_assertok(scan_mmc_bootdev(uts, "mmc5", true));
ut_assertok(run_command("bootflow list", 0));
ut_assert_nextlinen("Showing all");

View file

@ -6,197 +6,93 @@
"""See README for more information"""
from argparse import ArgumentParser
try:
import importlib.resources
from importlib import resources
except ImportError:
# for Python 3.6
import importlib_resources
import importlib_resources as resources
import os
import re
import sys
import traceback
if __name__ == "__main__":
# Allow 'from patman import xxx to work'
our_path = os.path.dirname(os.path.realpath(__file__))
sys.path.append(os.path.join(our_path, '..'))
# Allow 'from patman import xxx to work'
# pylint: disable=C0413
our_path = os.path.dirname(os.path.realpath(__file__))
sys.path.append(os.path.join(our_path, '..'))
# Our modules
from patman import cmdline
from patman import control
from patman import func_test
from patman import gitutil
from patman import project
from patman import settings
from u_boot_pylib import terminal
from u_boot_pylib import test_util
from u_boot_pylib import tools
epilog = '''Create patches from commits in a branch, check them and email them
as specified by tags you place in the commits. Use -n to do a dry run first.'''
parser = ArgumentParser(epilog=epilog)
parser.add_argument('-b', '--branch', type=str,
help="Branch to process (by default, the current branch)")
parser.add_argument('-c', '--count', dest='count', type=int,
default=-1, help='Automatically create patches from top n commits')
parser.add_argument('-e', '--end', type=int, default=0,
help='Commits to skip at end of patch list')
parser.add_argument('-D', '--debug', action='store_true',
help='Enabling debugging (provides a full traceback on error)')
parser.add_argument('-p', '--project', default=project.detect_project(),
help="Project name; affects default option values and "
"aliases [default: %(default)s]")
parser.add_argument('-P', '--patchwork-url',
default='https://patchwork.ozlabs.org',
help='URL of patchwork server [default: %(default)s]')
parser.add_argument('-s', '--start', dest='start', type=int,
default=0, help='Commit to start creating patches from (0 = HEAD)')
parser.add_argument('-v', '--verbose', action='store_true', dest='verbose',
default=False, help='Verbose output of errors and warnings')
parser.add_argument('-H', '--full-help', action='store_true', dest='full_help',
default=False, help='Display the README file')
def run_patman():
"""Run patamn
subparsers = parser.add_subparsers(dest='cmd')
send = subparsers.add_parser(
'send', help='Format, check and email patches (default command)')
send.add_argument('-i', '--ignore-errors', action='store_true',
dest='ignore_errors', default=False,
help='Send patches email even if patch errors are found')
send.add_argument('-l', '--limit-cc', dest='limit', type=int, default=None,
help='Limit the cc list to LIMIT entries [default: %(default)s]')
send.add_argument('-m', '--no-maintainers', action='store_false',
dest='add_maintainers', default=True,
help="Don't cc the file maintainers automatically")
send.add_argument(
'--get-maintainer-script', dest='get_maintainer_script', type=str,
action='store',
default=os.path.join(gitutil.get_top_level(), 'scripts',
'get_maintainer.pl') + ' --norolestats',
help='File name of the get_maintainer.pl (or compatible) script.')
send.add_argument('-n', '--dry-run', action='store_true', dest='dry_run',
default=False, help="Do a dry run (create but don't email patches)")
send.add_argument('-r', '--in-reply-to', type=str, action='store',
help="Message ID that this series is in reply to")
send.add_argument('-t', '--ignore-bad-tags', action='store_true',
default=False,
help='Ignore bad tags / aliases (default=warn)')
send.add_argument('-T', '--thread', action='store_true', dest='thread',
default=False, help='Create patches as a single thread')
send.add_argument('--cc-cmd', dest='cc_cmd', type=str, action='store',
default=None, help='Output cc list for patch file (used by git)')
send.add_argument('--no-binary', action='store_true', dest='ignore_binary',
default=False,
help="Do not output contents of changes in binary files")
send.add_argument('--no-check', action='store_false', dest='check_patch',
default=True,
help="Don't check for patch compliance")
send.add_argument('--tree', dest='check_patch_use_tree', default=False,
action='store_true',
help=("Set `tree` to True. If `tree` is False then we'll "
"pass '--no-tree' to checkpatch (default: tree=%(default)s)"))
send.add_argument('--no-tree', dest='check_patch_use_tree',
action='store_false', help="Set `tree` to False")
send.add_argument('--no-tags', action='store_false', dest='process_tags',
default=True, help="Don't process subject tags as aliases")
send.add_argument('--no-signoff', action='store_false', dest='add_signoff',
default=True, help="Don't add Signed-off-by to patches")
send.add_argument('--smtp-server', type=str,
help="Specify the SMTP server to 'git send-email'")
send.add_argument('--keep-change-id', action='store_true',
help='Preserve Change-Id tags in patches to send.')
This is the main program. It collects arguments and runs either the tests or
the control module.
"""
args = cmdline.parse_args()
send.add_argument('patchfiles', nargs='*')
if not args.debug:
sys.tracebacklimit = 0
# Only add the 'test' action if the test data files are available.
if os.path.exists(func_test.TEST_DATA_DIR):
test_parser = subparsers.add_parser('test', help='Run tests')
test_parser.add_argument('testname', type=str, default=None, nargs='?',
help="Specify the test to run")
# Run our meagre tests
if args.cmd == 'test':
# pylint: disable=C0415
from patman import func_test
from patman import test_checkpatch
status = subparsers.add_parser('status',
help='Check status of patches in patchwork')
status.add_argument('-C', '--show-comments', action='store_true',
help='Show comments from each patch')
status.add_argument('-d', '--dest-branch', type=str,
help='Name of branch to create with collected responses')
status.add_argument('-f', '--force', action='store_true',
help='Force overwriting an existing branch')
result = test_util.run_test_suites(
'patman', False, False, False, None, None, None,
[test_checkpatch.TestPatch, func_test.TestFunctional,
'gitutil', 'settings'])
# Parse options twice: first to get the project and second to handle
# defaults properly (which depends on project)
# Use parse_known_args() in case 'cmd' is omitted
argv = sys.argv[1:]
args, rest = parser.parse_known_args(argv)
if hasattr(args, 'project'):
settings.Setup(parser, args.project)
args, rest = parser.parse_known_args(argv)
sys.exit(0 if result.wasSuccessful() else 1)
# If we have a command, it is safe to parse all arguments
if args.cmd:
args = parser.parse_args(argv)
else:
# No command, so insert it after the known arguments and before the ones
# that presumably relate to the 'send' subcommand
nargs = len(rest)
argv = argv[:-nargs] + ['send'] + rest
args = parser.parse_args(argv)
# Process commits, produce patches files, check them, email them
elif args.cmd == 'send':
# Called from git with a patch filename as argument
# Printout a list of additional CC recipients for this patch
if args.cc_cmd:
re_line = re.compile(r'(\S*) (.*)')
with open(args.cc_cmd, 'r', encoding='utf-8') as inf:
for line in inf.readlines():
match = re_line.match(line)
if match and match.group(1) == args.patchfiles[0]:
for cca in match.group(2).split('\0'):
cca = cca.strip()
if cca:
print(cca)
if __name__ != "__main__":
pass
elif args.full_help:
with resources.path('patman', 'README.rst') as readme:
tools.print_full_help(str(readme))
else:
# If we are not processing tags, no need to warning about bad ones
if not args.process_tags:
args.ignore_bad_tags = True
control.send(args)
if not args.debug:
sys.tracebacklimit = 0
# Check status of patches in patchwork
elif args.cmd == 'status':
ret_code = 0
try:
control.patchwork_status(args.branch, args.count, args.start, args.end,
args.dest_branch, args.force,
args.show_comments, args.patchwork_url)
except Exception as exc:
terminal.tprint(f'patman: {type(exc).__name__}: {exc}',
colour=terminal.Color.RED)
if args.debug:
print()
traceback.print_exc()
ret_code = 1
sys.exit(ret_code)
# Run our meagre tests
if args.cmd == 'test':
from patman import func_test
from patman import test_checkpatch
result = test_util.run_test_suites(
'patman', False, False, False, None, None, None,
[test_checkpatch.TestPatch, func_test.TestFunctional,
'gitutil', 'settings'])
sys.exit(0 if result.wasSuccessful() else 1)
# Process commits, produce patches files, check them, email them
elif args.cmd == 'send':
# Called from git with a patch filename as argument
# Printout a list of additional CC recipients for this patch
if args.cc_cmd:
fd = open(args.cc_cmd, 'r')
re_line = re.compile('(\S*) (.*)')
for line in fd.readlines():
match = re_line.match(line)
if match and match.group(1) == args.patchfiles[0]:
for cc in match.group(2).split('\0'):
cc = cc.strip()
if cc:
print(cc)
fd.close()
elif args.full_help:
with importlib.resources.path('patman', 'README.rst') as readme:
tools.print_full_help(str(readme))
else:
# If we are not processing tags, no need to warning about bad ones
if not args.process_tags:
args.ignore_bad_tags = True
control.send(args)
# Check status of patches in patchwork
elif args.cmd == 'status':
ret_code = 0
try:
control.patchwork_status(args.branch, args.count, args.start, args.end,
args.dest_branch, args.force,
args.show_comments, args.patchwork_url)
except Exception as e:
terminal.tprint('patman: %s: %s' % (type(e).__name__, e),
colour=terminal.Color.RED)
if args.debug:
print()
traceback.print_exc()
ret_code = 1
sys.exit(ret_code)
if __name__ == "__main__":
sys.exit(run_patman())

147
tools/patman/cmdline.py Normal file
View file

@ -0,0 +1,147 @@
# SPDX-License-Identifier: GPL-2.0+
#
# Copyright 2023 Google LLC
#
"""Handles parsing of buildman arguments
This creates the argument parser and uses it to parse the arguments passed in
"""
import argparse
import os
import pathlib
import sys
from patman import gitutil
from patman import project
from patman import settings
PATMAN_DIR = pathlib.Path(__file__).parent
HAS_TESTS = os.path.exists(PATMAN_DIR / "func_test.py")
def parse_args():
"""Parse command line arguments from sys.argv[]
Returns:
tuple containing:
options: command line options
args: command lin arguments
"""
epilog = '''Create patches from commits in a branch, check them and email
them as specified by tags you place in the commits. Use -n to do a dry
run first.'''
parser = argparse.ArgumentParser(epilog=epilog)
parser.add_argument('-b', '--branch', type=str,
help="Branch to process (by default, the current branch)")
parser.add_argument('-c', '--count', dest='count', type=int,
default=-1, help='Automatically create patches from top n commits')
parser.add_argument('-e', '--end', type=int, default=0,
help='Commits to skip at end of patch list')
parser.add_argument('-D', '--debug', action='store_true',
help='Enabling debugging (provides a full traceback on error)')
parser.add_argument('-p', '--project', default=project.detect_project(),
help="Project name; affects default option values and "
"aliases [default: %(default)s]")
parser.add_argument('-P', '--patchwork-url',
default='https://patchwork.ozlabs.org',
help='URL of patchwork server [default: %(default)s]')
parser.add_argument('-s', '--start', dest='start', type=int,
default=0, help='Commit to start creating patches from (0 = HEAD)')
parser.add_argument(
'-v', '--verbose', action='store_true', dest='verbose', default=False,
help='Verbose output of errors and warnings')
parser.add_argument(
'-H', '--full-help', action='store_true', dest='full_help',
default=False, help='Display the README file')
subparsers = parser.add_subparsers(dest='cmd')
send = subparsers.add_parser(
'send', help='Format, check and email patches (default command)')
send.add_argument('-i', '--ignore-errors', action='store_true',
dest='ignore_errors', default=False,
help='Send patches email even if patch errors are found')
send.add_argument('-l', '--limit-cc', dest='limit', type=int, default=None,
help='Limit the cc list to LIMIT entries [default: %(default)s]')
send.add_argument('-m', '--no-maintainers', action='store_false',
dest='add_maintainers', default=True,
help="Don't cc the file maintainers automatically")
send.add_argument(
'--get-maintainer-script', dest='get_maintainer_script', type=str,
action='store',
default=os.path.join(gitutil.get_top_level(), 'scripts',
'get_maintainer.pl') + ' --norolestats',
help='File name of the get_maintainer.pl (or compatible) script.')
send.add_argument('-n', '--dry-run', action='store_true', dest='dry_run',
default=False, help="Do a dry run (create but don't email patches)")
send.add_argument('-r', '--in-reply-to', type=str, action='store',
help="Message ID that this series is in reply to")
send.add_argument('-t', '--ignore-bad-tags', action='store_true',
default=False,
help='Ignore bad tags / aliases (default=warn)')
send.add_argument('-T', '--thread', action='store_true', dest='thread',
default=False, help='Create patches as a single thread')
send.add_argument('--cc-cmd', dest='cc_cmd', type=str, action='store',
default=None, help='Output cc list for patch file (used by git)')
send.add_argument('--no-binary', action='store_true', dest='ignore_binary',
default=False,
help="Do not output contents of changes in binary files")
send.add_argument('--no-check', action='store_false', dest='check_patch',
default=True,
help="Don't check for patch compliance")
send.add_argument(
'--tree', dest='check_patch_use_tree', default=False,
action='store_true',
help=("Set `tree` to True. If `tree` is False then we'll pass "
"'--no-tree' to checkpatch (default: tree=%(default)s)"))
send.add_argument('--no-tree', dest='check_patch_use_tree',
action='store_false', help="Set `tree` to False")
send.add_argument(
'--no-tags', action='store_false', dest='process_tags', default=True,
help="Don't process subject tags as aliases")
send.add_argument('--no-signoff', action='store_false', dest='add_signoff',
default=True, help="Don't add Signed-off-by to patches")
send.add_argument('--smtp-server', type=str,
help="Specify the SMTP server to 'git send-email'")
send.add_argument('--keep-change-id', action='store_true',
help='Preserve Change-Id tags in patches to send.')
send.add_argument('patchfiles', nargs='*')
# Only add the 'test' action if the test data files are available.
if HAS_TESTS:
test_parser = subparsers.add_parser('test', help='Run tests')
test_parser.add_argument('testname', type=str, default=None, nargs='?',
help="Specify the test to run")
status = subparsers.add_parser('status',
help='Check status of patches in patchwork')
status.add_argument('-C', '--show-comments', action='store_true',
help='Show comments from each patch')
status.add_argument(
'-d', '--dest-branch', type=str,
help='Name of branch to create with collected responses')
status.add_argument('-f', '--force', action='store_true',
help='Force overwriting an existing branch')
# Parse options twice: first to get the project and second to handle
# defaults properly (which depends on project)
# Use parse_known_args() in case 'cmd' is omitted
argv = sys.argv[1:]
args, rest = parser.parse_known_args(argv)
if hasattr(args, 'project'):
settings.Setup(parser, args.project)
args, rest = parser.parse_known_args(argv)
# If we have a command, it is safe to parse all arguments
if args.cmd:
args = parser.parse_args(argv)
else:
# No command, so insert it after the known arguments and before the ones
# that presumably relate to the 'send' subcommand
nargs = len(rest)
argv = argv[:-nargs] + ['send'] + rest
args = parser.parse_args(argv)
return args