blob: f3d9c0c4348657540a1fdfb35621816b55080289 [file] [log] [blame]
Simon Glasse3986d92019-10-31 07:42:52 -06001#!/usr/bin/env python3
Tom Rini83d290c2018-05-06 17:58:06 -04002# SPDX-License-Identifier: GPL-2.0+
Simon Glass0d24de92012-01-14 15:12:45 +00003#
4# Copyright (c) 2011 The Chromium OS Authors.
5#
Simon Glass0d24de92012-01-14 15:12:45 +00006
7"""See README for more information"""
8
9from optparse import OptionParser
10import os
11import re
12import sys
13import unittest
14
Simon Glass0d7a8c42020-04-17 18:08:52 -060015if __name__ == "__main__":
16 # Allow 'from patman import xxx to work'
17 our_path = os.path.dirname(os.path.realpath(__file__))
18 sys.path.append(os.path.join(our_path, '..'))
19
Simon Glass0d24de92012-01-14 15:12:45 +000020# Our modules
Simon Glass0d7a8c42020-04-17 18:08:52 -060021from patman import checkpatch
22from patman import command
23from patman import gitutil
24from patman import patchstream
25from patman import project
26from patman import settings
27from patman import terminal
28from patman import test
Simon Glass0d24de92012-01-14 15:12:45 +000029
30
31parser = OptionParser()
32parser.add_option('-H', '--full-help', action='store_true', dest='full_help',
33 default=False, help='Display the README file')
34parser.add_option('-c', '--count', dest='count', type='int',
35 default=-1, help='Automatically create patches from top n commits')
36parser.add_option('-i', '--ignore-errors', action='store_true',
37 dest='ignore_errors', default=False,
38 help='Send patches email even if patch errors are found')
Simon Glass983a2742014-09-14 20:23:17 -060039parser.add_option('-m', '--no-maintainers', action='store_false',
40 dest='add_maintainers', default=True,
41 help="Don't cc the file maintainers automatically")
Chris Packham4fb35022018-06-07 20:45:06 +120042parser.add_option('-l', '--limit-cc', dest='limit', type='int',
43 default=None, help='Limit the cc list to LIMIT entries [default: %default]')
Simon Glass0d24de92012-01-14 15:12:45 +000044parser.add_option('-n', '--dry-run', action='store_true', dest='dry_run',
Simon Glassca706e72013-03-26 13:09:45 +000045 default=False, help="Do a dry run (create but don't email patches)")
Vadim Bendebury99adf6e2013-01-09 16:00:10 +000046parser.add_option('-p', '--project', default=project.DetectProject(),
47 help="Project name; affects default option values and "
48 "aliases [default: %default]")
Doug Anderson6d819922013-03-17 10:31:04 +000049parser.add_option('-r', '--in-reply-to', type='string', action='store',
50 help="Message ID that this series is in reply to")
Simon Glass0d24de92012-01-14 15:12:45 +000051parser.add_option('-s', '--start', dest='start', type='int',
52 default=0, help='Commit to start creating patches from (0 = HEAD)')
Simon Glassa1318f72013-03-26 13:09:42 +000053parser.add_option('-t', '--ignore-bad-tags', action='store_true',
54 default=False, help='Ignore bad tags / aliases')
55parser.add_option('--test', action='store_true', dest='test',
Simon Glass0d24de92012-01-14 15:12:45 +000056 default=False, help='run tests')
57parser.add_option('-v', '--verbose', action='store_true', dest='verbose',
58 default=False, help='Verbose output of errors and warnings')
59parser.add_option('--cc-cmd', dest='cc_cmd', type='string', action='store',
60 default=None, help='Output cc list for patch file (used by git)')
Vadim Bendebury99adf6e2013-01-09 16:00:10 +000061parser.add_option('--no-check', action='store_false', dest='check_patch',
62 default=True,
63 help="Don't check for patch compliance")
Simon Glass0d24de92012-01-14 15:12:45 +000064parser.add_option('--no-tags', action='store_false', dest='process_tags',
65 default=True, help="Don't process subject tags as aliaes")
Simon Glassa60aedf2018-06-19 09:56:07 -060066parser.add_option('--smtp-server', type='str',
67 help="Specify the SMTP server to 'git send-email'")
Mateusz Kulikowski27067a42016-01-14 20:37:41 +010068parser.add_option('-T', '--thread', action='store_true', dest='thread',
69 default=False, help='Create patches as a single thread')
Simon Glass0d24de92012-01-14 15:12:45 +000070
Masahiro Yamadae0a4d062014-08-21 14:28:03 +090071parser.usage += """
Simon Glass0d24de92012-01-14 15:12:45 +000072
73Create patches from commits in a branch, check them and email them as
Simon Glassca706e72013-03-26 13:09:45 +000074specified by tags you place in the commits. Use -n to do a dry run first."""
Simon Glass0d24de92012-01-14 15:12:45 +000075
Doug Anderson8568bae2012-12-03 14:43:17 +000076
Doug Andersona1dcee82012-12-03 14:43:18 +000077# Parse options twice: first to get the project and second to handle
78# defaults properly (which depends on project).
79(options, args) = parser.parse_args()
80settings.Setup(parser, options.project, '')
Simon Glass0d24de92012-01-14 15:12:45 +000081(options, args) = parser.parse_args()
82
Simon Glass9649e152015-07-30 13:47:41 -060083if __name__ != "__main__":
84 pass
85
Simon Glass0d24de92012-01-14 15:12:45 +000086# Run our meagre tests
Simon Glass9649e152015-07-30 13:47:41 -060087elif options.test:
Simon Glass0d24de92012-01-14 15:12:45 +000088 import doctest
Simon Glass0d7a8c42020-04-17 18:08:52 -060089 from patman import func_test
Simon Glass0d24de92012-01-14 15:12:45 +000090
91 sys.argv = [sys.argv[0]]
Simon Glass0d24de92012-01-14 15:12:45 +000092 result = unittest.TestResult()
Simon Glass6e87ae12017-05-29 15:31:31 -060093 for module in (test.TestPatch, func_test.TestFunctional):
94 suite = unittest.TestLoader().loadTestsFromTestCase(module)
95 suite.run(result)
Simon Glass0d24de92012-01-14 15:12:45 +000096
Simon Glass37b224f2020-04-09 15:08:40 -060097 for module in ['gitutil', 'settings', 'terminal']:
Doug Anderson656cffe2012-12-03 14:43:19 +000098 suite = doctest.DocTestSuite(module)
99 suite.run(result)
Simon Glass0d24de92012-01-14 15:12:45 +0000100
101 # TODO: Surely we can just 'print' result?
Paul Burtona920a172016-09-27 16:03:50 +0100102 print(result)
Simon Glass0d24de92012-01-14 15:12:45 +0000103 for test, err in result.errors:
Paul Burtona920a172016-09-27 16:03:50 +0100104 print(err)
Simon Glass0d24de92012-01-14 15:12:45 +0000105 for test, err in result.failures:
Paul Burtona920a172016-09-27 16:03:50 +0100106 print(err)
Simon Glass0d24de92012-01-14 15:12:45 +0000107
108# Called from git with a patch filename as argument
109# Printout a list of additional CC recipients for this patch
110elif options.cc_cmd:
111 fd = open(options.cc_cmd, 'r')
112 re_line = re.compile('(\S*) (.*)')
113 for line in fd.readlines():
114 match = re_line.match(line)
115 if match and match.group(1) == args[0]:
Dmitry Torokhov8ab452d2019-10-21 20:09:56 -0700116 for cc in match.group(2).split('\0'):
Simon Glass0d24de92012-01-14 15:12:45 +0000117 cc = cc.strip()
118 if cc:
Paul Burtona920a172016-09-27 16:03:50 +0100119 print(cc)
Simon Glass0d24de92012-01-14 15:12:45 +0000120 fd.close()
121
122elif options.full_help:
123 pager = os.getenv('PAGER')
124 if not pager:
125 pager = 'more'
Simon Glass2bdeade2016-03-06 19:45:34 -0700126 fname = os.path.join(os.path.dirname(os.path.realpath(sys.argv[0])),
127 'README')
Simon Glass0d24de92012-01-14 15:12:45 +0000128 command.Run(pager, fname)
129
130# Process commits, produce patches files, check them, email them
131else:
132 gitutil.Setup()
133
134 if options.count == -1:
135 # Work out how many patches to send if we can
136 options.count = gitutil.CountCommitsToBranch() - options.start
137
138 col = terminal.Color()
139 if not options.count:
140 str = 'No commits found to process - please use -c flag'
Masahiro Yamada31e21412014-08-16 00:59:26 +0900141 sys.exit(col.Color(col.RED, str))
Simon Glass0d24de92012-01-14 15:12:45 +0000142
143 # Read the metadata from the commits
144 if options.count:
145 series = patchstream.GetMetaData(options.start, options.count)
146 cover_fname, args = gitutil.CreatePatches(options.start, options.count,
147 series)
148
149 # Fix up the patch files to our liking, and insert the cover letter
Simon Glassdb116cc2017-05-29 15:31:27 -0600150 patchstream.FixPatches(series, args)
151 if cover_fname and series.get('cover'):
Simon Glass0d24de92012-01-14 15:12:45 +0000152 patchstream.InsertCoverLetter(cover_fname, series, options.count)
153
154 # Do a few checks on the series
155 series.DoChecks()
156
157 # Check the patches, and run them through 'git am' just to be sure
Vadim Bendebury99adf6e2013-01-09 16:00:10 +0000158 if options.check_patch:
159 ok = checkpatch.CheckPatches(options.verbose, args)
160 else:
161 ok = True
Simon Glass0d24de92012-01-14 15:12:45 +0000162
Simon Glassa1318f72013-03-26 13:09:42 +0000163 cc_file = series.MakeCcFile(options.process_tags, cover_fname,
Simon Glass983a2742014-09-14 20:23:17 -0600164 not options.ignore_bad_tags,
Chris Packham4fb35022018-06-07 20:45:06 +1200165 options.add_maintainers, options.limit)
Doug Andersond94566a2012-12-03 14:40:42 +0000166
Simon Glass0d24de92012-01-14 15:12:45 +0000167 # Email the patches out (giving the user time to check / cancel)
168 cmd = ''
Vadim Bendebury1f727882014-09-04 10:45:13 -0700169 its_a_go = ok or options.ignore_errors
170 if its_a_go:
Simon Glass0d24de92012-01-14 15:12:45 +0000171 cmd = gitutil.EmailPatches(series, cover_fname, args,
Simon Glassa1318f72013-03-26 13:09:42 +0000172 options.dry_run, not options.ignore_bad_tags, cc_file,
Simon Glassa60aedf2018-06-19 09:56:07 -0600173 in_reply_to=options.in_reply_to, thread=options.thread,
174 smtp_server=options.smtp_server)
Vadim Bendebury1f727882014-09-04 10:45:13 -0700175 else:
Paul Burtona920a172016-09-27 16:03:50 +0100176 print(col.Color(col.RED, "Not sending emails due to errors/warnings"))
Simon Glass0d24de92012-01-14 15:12:45 +0000177
178 # For a dry run, just show our actions as a sanity check
179 if options.dry_run:
180 series.ShowActions(args, cmd, options.process_tags)
Vadim Bendebury1f727882014-09-04 10:45:13 -0700181 if not its_a_go:
Paul Burtona920a172016-09-27 16:03:50 +0100182 print(col.Color(col.RED, "Email would not be sent"))
Doug Andersond94566a2012-12-03 14:40:42 +0000183
184 os.remove(cc_file)