Merge tag 'u-boot-atmel-2021.07-a' of https://source.denx.de/u-boot/custodians/u...
[platform/kernel/u-boot.git] / tools / patman / main.py
1 #!/usr/bin/env python3
2 # SPDX-License-Identifier: GPL-2.0+
3 #
4 # Copyright (c) 2011 The Chromium OS Authors.
5 #
6
7 """See README for more information"""
8
9 from argparse import ArgumentParser
10 import os
11 import re
12 import shutil
13 import sys
14 import traceback
15 import unittest
16
17 if __name__ == "__main__":
18     # Allow 'from patman import xxx to work'
19     our_path = os.path.dirname(os.path.realpath(__file__))
20     sys.path.append(os.path.join(our_path, '..'))
21
22 # Our modules
23 from patman import command
24 from patman import control
25 from patman import gitutil
26 from patman import project
27 from patman import settings
28 from patman import terminal
29 from patman import test_util
30 from patman import test_checkpatch
31
32 epilog = '''Create patches from commits in a branch, check them and email them
33 as specified by tags you place in the commits. Use -n to do a dry run first.'''
34
35 parser = ArgumentParser(epilog=epilog)
36 parser.add_argument('-b', '--branch', type=str,
37     help="Branch to process (by default, the current branch)")
38 parser.add_argument('-c', '--count', dest='count', type=int,
39     default=-1, help='Automatically create patches from top n commits')
40 parser.add_argument('-e', '--end', type=int, default=0,
41     help='Commits to skip at end of patch list')
42 parser.add_argument('-D', '--debug', action='store_true',
43     help='Enabling debugging (provides a full traceback on error)')
44 parser.add_argument('-p', '--project', default=project.DetectProject(),
45                     help="Project name; affects default option values and "
46                     "aliases [default: %(default)s]")
47 parser.add_argument('-P', '--patchwork-url',
48                     default='https://patchwork.ozlabs.org',
49                     help='URL of patchwork server [default: %(default)s]')
50 parser.add_argument('-s', '--start', dest='start', type=int,
51     default=0, help='Commit to start creating patches from (0 = HEAD)')
52 parser.add_argument('-v', '--verbose', action='store_true', dest='verbose',
53                     default=False, help='Verbose output of errors and warnings')
54 parser.add_argument('-H', '--full-help', action='store_true', dest='full_help',
55                     default=False, help='Display the README file')
56
57 subparsers = parser.add_subparsers(dest='cmd')
58 send = subparsers.add_parser('send')
59 send.add_argument('-i', '--ignore-errors', action='store_true',
60        dest='ignore_errors', default=False,
61        help='Send patches email even if patch errors are found')
62 send.add_argument('-l', '--limit-cc', dest='limit', type=int, default=None,
63        help='Limit the cc list to LIMIT entries [default: %(default)s]')
64 send.add_argument('-m', '--no-maintainers', action='store_false',
65        dest='add_maintainers', default=True,
66        help="Don't cc the file maintainers automatically")
67 send.add_argument('-n', '--dry-run', action='store_true', dest='dry_run',
68        default=False, help="Do a dry run (create but don't email patches)")
69 send.add_argument('-r', '--in-reply-to', type=str, action='store',
70                   help="Message ID that this series is in reply to")
71 send.add_argument('-t', '--ignore-bad-tags', action='store_true',
72                   default=False,
73                   help='Ignore bad tags / aliases (default=warn)')
74 send.add_argument('-T', '--thread', action='store_true', dest='thread',
75                   default=False, help='Create patches as a single thread')
76 send.add_argument('--cc-cmd', dest='cc_cmd', type=str, action='store',
77        default=None, help='Output cc list for patch file (used by git)')
78 send.add_argument('--no-binary', action='store_true', dest='ignore_binary',
79                   default=False,
80                   help="Do not output contents of changes in binary files")
81 send.add_argument('--no-check', action='store_false', dest='check_patch',
82                   default=True,
83                   help="Don't check for patch compliance")
84 send.add_argument('--no-tags', action='store_false', dest='process_tags',
85                   default=True, help="Don't process subject tags as aliases")
86 send.add_argument('--no-signoff', action='store_false', dest='add_signoff',
87                   default=True, help="Don't add Signed-off-by to patches")
88 send.add_argument('--smtp-server', type=str,
89                   help="Specify the SMTP server to 'git send-email'")
90
91 send.add_argument('patchfiles', nargs='*')
92
93 test_parser = subparsers.add_parser('test', help='Run tests')
94 test_parser.add_argument('testname', type=str, default=None, nargs='?',
95                          help="Specify the test to run")
96
97 status = subparsers.add_parser('status',
98                                help='Check status of patches in patchwork')
99 status.add_argument('-C', '--show-comments', action='store_true',
100                     help='Show comments from each patch')
101 status.add_argument('-d', '--dest-branch', type=str,
102                     help='Name of branch to create with collected responses')
103 status.add_argument('-f', '--force', action='store_true',
104                     help='Force overwriting an existing branch')
105
106 # Parse options twice: first to get the project and second to handle
107 # defaults properly (which depends on project)
108 # Use parse_known_args() in case 'cmd' is omitted
109 argv = sys.argv[1:]
110 args, rest = parser.parse_known_args(argv)
111 if hasattr(args, 'project'):
112     settings.Setup(gitutil, parser, args.project, '')
113     args, rest = parser.parse_known_args(argv)
114
115 # If we have a command, it is safe to parse all arguments
116 if args.cmd:
117     args = parser.parse_args(argv)
118 else:
119     # No command, so insert it after the known arguments and before the ones
120     # that presumably relate to the 'send' subcommand
121     nargs = len(rest)
122     argv = argv[:-nargs] + ['send'] + rest
123     args = parser.parse_args(argv)
124
125 if __name__ != "__main__":
126     pass
127
128 if not args.debug:
129     sys.tracebacklimit = 0
130
131 # Run our meagre tests
132 if args.cmd == 'test':
133     import doctest
134     from patman import func_test
135
136     sys.argv = [sys.argv[0]]
137     result = unittest.TestResult()
138     suite = unittest.TestSuite()
139     loader = unittest.TestLoader()
140     for module in (test_checkpatch.TestPatch, func_test.TestFunctional):
141         if args.testname:
142             try:
143                 suite.addTests(loader.loadTestsFromName(args.testname, module))
144             except AttributeError:
145                 continue
146         else:
147             suite.addTests(loader.loadTestsFromTestCase(module))
148     suite.run(result)
149
150     for module in ['gitutil', 'settings', 'terminal']:
151         suite = doctest.DocTestSuite(module)
152         suite.run(result)
153
154     sys.exit(test_util.ReportResult('patman', args.testname, result))
155
156 # Process commits, produce patches files, check them, email them
157 elif args.cmd == 'send':
158     # Called from git with a patch filename as argument
159     # Printout a list of additional CC recipients for this patch
160     if args.cc_cmd:
161         fd = open(args.cc_cmd, 'r')
162         re_line = re.compile('(\S*) (.*)')
163         for line in fd.readlines():
164             match = re_line.match(line)
165             if match and match.group(1) == args.patchfiles[0]:
166                 for cc in match.group(2).split('\0'):
167                     cc = cc.strip()
168                     if cc:
169                         print(cc)
170         fd.close()
171
172     elif args.full_help:
173         pager = os.getenv('PAGER')
174         if not pager:
175             pager = shutil.which('less')
176         if not pager:
177             pager = 'more'
178         fname = os.path.join(os.path.dirname(os.path.realpath(sys.argv[0])),
179                              'README')
180         command.Run(pager, fname)
181
182     else:
183         # If we are not processing tags, no need to warning about bad ones
184         if not args.process_tags:
185             args.ignore_bad_tags = True
186         control.send(args)
187
188 # Check status of patches in patchwork
189 elif args.cmd == 'status':
190     ret_code = 0
191     try:
192         control.patchwork_status(args.branch, args.count, args.start, args.end,
193                                  args.dest_branch, args.force,
194                                  args.show_comments, args.patchwork_url)
195     except Exception as e:
196         terminal.Print('patman: %s: %s' % (type(e).__name__, e),
197                        colour=terminal.Color.RED)
198         if args.debug:
199             print()
200             traceback.print_exc()
201         ret_code = 1
202     sys.exit(ret_code)