blob: e67867b845c036845dfa90297024a890424bf337 [file] [log] [blame]
Simon Glass7d5b04e2020-07-05 21:41:49 -06001# SPDX-License-Identifier: GPL-2.0+
2#
3# Copyright 2020 Google LLC
4#
5"""Handles the main control logic of patman
6
7This module provides various functions called by the main program to implement
8the features of patman.
9"""
10
11import os
12import sys
13
14from patman import checkpatch
15from patman import gitutil
16from patman import patchstream
17from patman import terminal
18
19def setup():
20 """Do required setup before doing anything"""
21 gitutil.Setup()
22
Simon Glass137947e2020-07-05 21:41:52 -060023def prepare_patches(col, branch, count, start, end, ignore_binary):
Simon Glass7d5b04e2020-07-05 21:41:49 -060024 """Figure out what patches to generate, then generate them
25
26 The patch files are written to the current directory, e.g. 0001_xxx.patch
27 0002_yyy.patch
28
29 Args:
30 col (terminal.Color): Colour output object
Simon Glass262130f2020-07-05 21:41:51 -060031 branch (str): Branch to create patches from (None = current)
Simon Glass7d5b04e2020-07-05 21:41:49 -060032 count (int): Number of patches to produce, or -1 to produce patches for
33 the current branch back to the upstream commit
34 start (int): Start partch to use (0=first / top of branch)
Simon Glass137947e2020-07-05 21:41:52 -060035 end (int): End patch to use (0=last one in series, 1=one before that,
36 etc.)
Simon Glass7d5b04e2020-07-05 21:41:49 -060037 ignore_binary (bool): Don't generate patches for binary files
38
39 Returns:
40 Tuple:
41 Series object for this series (set of patches)
42 Filename of the cover letter as a string (None if none)
43 patch_files: List of patch filenames, each a string, e.g.
44 ['0001_xxx.patch', '0002_yyy.patch']
45 """
46 if count == -1:
47 # Work out how many patches to send if we can
Simon Glass262130f2020-07-05 21:41:51 -060048 count = (gitutil.CountCommitsToBranch(branch) - start)
Simon Glass7d5b04e2020-07-05 21:41:49 -060049
50 if not count:
51 sys.exit(col.Color(col.RED,
52 'No commits found to process - please use -c flag'))
53
54 # Read the metadata from the commits
Simon Glass137947e2020-07-05 21:41:52 -060055 to_do = count - end
Simon Glass262130f2020-07-05 21:41:51 -060056 series = patchstream.GetMetaData(branch, start, to_do)
Simon Glass7d5b04e2020-07-05 21:41:49 -060057 cover_fname, patch_files = gitutil.CreatePatches(
Simon Glass262130f2020-07-05 21:41:51 -060058 branch, start, to_do, ignore_binary, series)
Simon Glass7d5b04e2020-07-05 21:41:49 -060059
60 # Fix up the patch files to our liking, and insert the cover letter
61 patchstream.FixPatches(series, patch_files)
62 if cover_fname and series.get('cover'):
63 patchstream.InsertCoverLetter(cover_fname, series, to_do)
64 return series, cover_fname, patch_files
65
66def check_patches(series, patch_files, run_checkpatch, verbose):
67 """Run some checks on a set of patches
68
69 This santiy-checks the patman tags like Series-version and runs the patches
70 through checkpatch
71
72 Args:
73 series (Series): Series object for this series (set of patches)
74 patch_files (list): List of patch filenames, each a string, e.g.
75 ['0001_xxx.patch', '0002_yyy.patch']
76 run_checkpatch (bool): True to run checkpatch.pl
77 verbose (bool): True to print out every line of the checkpatch output as
78 it is parsed
79
80 Returns:
81 bool: True if the patches had no errors, False if they did
82 """
83 # Do a few checks on the series
84 series.DoChecks()
85
86 # Check the patches, and run them through 'git am' just to be sure
87 if run_checkpatch:
88 ok = checkpatch.CheckPatches(verbose, patch_files)
89 else:
90 ok = True
91 return ok
92
93
94def email_patches(col, series, cover_fname, patch_files, process_tags, its_a_go,
95 ignore_bad_tags, add_maintainers, limit, dry_run, in_reply_to,
96 thread, smtp_server):
97 """Email patches to the recipients
98
99 This emails out the patches and cover letter using 'git send-email'. Each
100 patch is copied to recipients identified by the patch tag and output from
101 the get_maintainer.pl script. The cover letter is copied to all recipients
102 of any patch.
103
104 To make this work a CC file is created holding the recipients for each patch
105 and the cover letter. See the main program 'cc_cmd' for this logic.
106
107 Args:
108 col (terminal.Color): Colour output object
109 series (Series): Series object for this series (set of patches)
110 cover_fname (str): Filename of the cover letter as a string (None if
111 none)
112 patch_files (list): List of patch filenames, each a string, e.g.
113 ['0001_xxx.patch', '0002_yyy.patch']
114 process_tags (bool): True to process subject tags in each patch, e.g.
115 for 'dm: spi: Add SPI support' this would be 'dm' and 'spi'. The
116 tags are looked up in the configured sendemail.aliasesfile and also
117 in ~/.patman (see README)
118 its_a_go (bool): True if we are going to actually send the patches,
119 False if the patches have errors and will not be sent unless
120 @ignore_errors
121 ignore_bad_tags (bool): True to just print a warning for unknown tags,
122 False to halt with an error
123 add_maintainers (bool): Run the get_maintainer.pl script for each patch
124 limit (int): Limit on the number of people that can be cc'd on a single
125 patch or the cover letter (None if no limit)
126 dry_run (bool): Don't actually email the patches, just print out what
127 would be sent
128 in_reply_to (str): If not None we'll pass this to git as --in-reply-to.
129 Should be a message ID that this is in reply to.
130 thread (bool): True to add --thread to git send-email (make all patches
131 reply to cover-letter or first patch in series)
132 smtp_server (str): SMTP server to use to send patches (None for default)
133 """
134 cc_file = series.MakeCcFile(process_tags, cover_fname, not ignore_bad_tags,
135 add_maintainers, limit)
136
137 # Email the patches out (giving the user time to check / cancel)
138 cmd = ''
139 if its_a_go:
140 cmd = gitutil.EmailPatches(
141 series, cover_fname, patch_files, dry_run, not ignore_bad_tags,
142 cc_file, in_reply_to=in_reply_to, thread=thread,
143 smtp_server=smtp_server)
144 else:
145 print(col.Color(col.RED, "Not sending emails due to errors/warnings"))
146
147 # For a dry run, just show our actions as a sanity check
148 if dry_run:
149 series.ShowActions(patch_files, cmd, process_tags)
150 if not its_a_go:
151 print(col.Color(col.RED, "Email would not be sent"))
152
153 os.remove(cc_file)
154
Simon Glassfda1e372020-07-05 21:41:53 -0600155def send(args):
Simon Glass7d5b04e2020-07-05 21:41:49 -0600156 """Create, check and send patches by email
157
158 Args:
Simon Glassfda1e372020-07-05 21:41:53 -0600159 args (argparse.Namespace): Arguments to patman
Simon Glass7d5b04e2020-07-05 21:41:49 -0600160 """
161 setup()
162 col = terminal.Color()
163 series, cover_fname, patch_files = prepare_patches(
Simon Glassfda1e372020-07-05 21:41:53 -0600164 col, args.branch, args.count, args.start, args.end,
165 args.ignore_binary)
166 ok = check_patches(series, patch_files, args.check_patch,
167 args.verbose)
Simon Glass262130f2020-07-05 21:41:51 -0600168
Simon Glassfda1e372020-07-05 21:41:53 -0600169 its_a_go = ok or args.ignore_errors
Simon Glass7d5b04e2020-07-05 21:41:49 -0600170 if its_a_go:
171 email_patches(
Simon Glassfda1e372020-07-05 21:41:53 -0600172 col, series, cover_fname, patch_files, args.process_tags,
173 its_a_go, args.ignore_bad_tags, args.add_maintainers,
174 args.limit, args.dry_run, args.in_reply_to, args.thread,
175 args.smtp_server)