blob: df3eb7483bb2f5f95770bc7d2d1ae24fe059d21f [file] [log] [blame]
Tom Rini10e47792018-05-06 17:58:06 -04001# SPDX-License-Identifier: GPL-2.0+
Simon Glass26132882012-01-14 15:12:45 +00002# Copyright (c) 2011 The Chromium OS Authors.
3#
Simon Glass26132882012-01-14 15:12:45 +00004
Douglas Anderson52b5ee82019-09-27 09:23:56 -07005import datetime
Wu, Josh393aaa22015-04-03 10:51:17 +08006import math
Simon Glass26132882012-01-14 15:12:45 +00007import os
8import re
9import shutil
10import tempfile
11
12import command
13import commit
14import gitutil
15from series import Series
16
17# Tags that we detect and remove
Douglas Anderson52b5ee82019-09-27 09:23:56 -070018re_remove = re.compile('^BUG=|^TEST=|^BRANCH=|^Review URL:'
Simon Glass5da2a782013-04-03 11:01:39 +000019 '|Reviewed-on:|Commit-\w*:')
Simon Glass26132882012-01-14 15:12:45 +000020
21# Lines which are allowed after a TEST= line
22re_allowed_after_test = re.compile('^Signed-off-by:')
23
Ilya Yanok644c1842012-08-06 23:46:05 +000024# Signoffs
Simon Glass46b34212014-04-20 10:50:14 -060025re_signoff = re.compile('^Signed-off-by: *(.*)')
Ilya Yanok644c1842012-08-06 23:46:05 +000026
Simon Glass26132882012-01-14 15:12:45 +000027# The start of the cover letter
28re_cover = re.compile('^Cover-letter:')
29
Simon Glassc72f3da2013-03-20 16:43:00 +000030# A cover letter Cc
31re_cover_cc = re.compile('^Cover-letter-cc: *(.*)')
32
Simon Glass26132882012-01-14 15:12:45 +000033# Patch series tag
Albert ARIBAUDd880efd2013-11-12 11:14:41 +010034re_series_tag = re.compile('^Series-([a-z-]*): *(.*)')
35
Douglas Anderson52b5ee82019-09-27 09:23:56 -070036# Change-Id will be used to generate the Message-Id and then be stripped
37re_change_id = re.compile('^Change-Id: *(.*)')
38
Albert ARIBAUDd880efd2013-11-12 11:14:41 +010039# Commit series tag
40re_commit_tag = re.compile('^Commit-([a-z-]*): *(.*)')
Simon Glass26132882012-01-14 15:12:45 +000041
42# Commit tags that we want to collect and keep
Simon Glassf7f01992014-02-16 08:23:47 -070043re_tag = re.compile('^(Tested-by|Acked-by|Reviewed-by|Patch-cc): (.*)')
Simon Glass26132882012-01-14 15:12:45 +000044
45# The start of a new commit in the git log
Doug Anderson21c325f2013-03-01 11:11:07 +000046re_commit = re.compile('^commit ([0-9a-f]*)$')
Simon Glass26132882012-01-14 15:12:45 +000047
48# We detect these since checkpatch doesn't always do it
49re_space_before_tab = re.compile('^[+].* \t')
50
51# States we can be in - can we use range() and still have comments?
52STATE_MSG_HEADER = 0 # Still in the message header
53STATE_PATCH_SUBJECT = 1 # In patch subject (first line of log for a commit)
54STATE_PATCH_HEADER = 2 # In patch header (after the subject)
55STATE_DIFFS = 3 # In the diff part (past --- line)
56
57class PatchStream:
58 """Class for detecting/injecting tags in a patch or series of patches
59
60 We support processing the output of 'git log' to read out the tags we
61 are interested in. We can also process a patch file in order to remove
62 unwanted tags or inject additional ones. These correspond to the two
63 phases of processing.
64 """
65 def __init__(self, series, name=None, is_log=False):
66 self.skip_blank = False # True to skip a single blank line
67 self.found_test = False # Found a TEST= line
68 self.lines_after_test = 0 # MNumber of lines found after TEST=
69 self.warn = [] # List of warnings we have collected
70 self.linenum = 1 # Output line number we are up to
71 self.in_section = None # Name of start...END section we are in
72 self.notes = [] # Series notes
73 self.section = [] # The current section...END section
74 self.series = series # Info about the patch series
75 self.is_log = is_log # True if indent like git log
76 self.in_change = 0 # Non-zero if we are in a change list
77 self.blank_count = 0 # Number of blank lines stored up
78 self.state = STATE_MSG_HEADER # What state are we in?
Simon Glass26132882012-01-14 15:12:45 +000079 self.signoff = [] # Contents of signoff line
80 self.commit = None # Current commit
81
82 def AddToSeries(self, line, name, value):
83 """Add a new Series-xxx tag.
84
85 When a Series-xxx tag is detected, we come here to record it, if we
86 are scanning a 'git log'.
87
88 Args:
89 line: Source line containing tag (useful for debug/error messages)
90 name: Tag name (part after 'Series-')
91 value: Tag value (part after 'Series-xxx: ')
92 """
93 if name == 'notes':
94 self.in_section = name
95 self.skip_blank = False
96 if self.is_log:
97 self.series.AddTag(self.commit, line, name, value)
98
Albert ARIBAUDd880efd2013-11-12 11:14:41 +010099 def AddToCommit(self, line, name, value):
100 """Add a new Commit-xxx tag.
101
102 When a Commit-xxx tag is detected, we come here to record it.
103
104 Args:
105 line: Source line containing tag (useful for debug/error messages)
106 name: Tag name (part after 'Commit-')
107 value: Tag value (part after 'Commit-xxx: ')
108 """
109 if name == 'notes':
110 self.in_section = 'commit-' + name
111 self.skip_blank = False
112
Simon Glass26132882012-01-14 15:12:45 +0000113 def CloseCommit(self):
114 """Save the current commit into our commit list, and reset our state"""
115 if self.commit and self.is_log:
116 self.series.AddCommit(self.commit)
117 self.commit = None
Bin Mengd23121c2016-06-26 23:24:30 -0700118 # If 'END' is missing in a 'Cover-letter' section, and that section
119 # happens to show up at the very end of the commit message, this is
120 # the chance for us to fix it up.
121 if self.in_section == 'cover' and self.is_log:
122 self.series.cover = self.section
123 self.in_section = None
124 self.skip_blank = True
125 self.section = []
Simon Glass26132882012-01-14 15:12:45 +0000126
Simon Glass26132882012-01-14 15:12:45 +0000127 def ProcessLine(self, line):
128 """Process a single line of a patch file or commit log
129
130 This process a line and returns a list of lines to output. The list
131 may be empty or may contain multiple output lines.
132
133 This is where all the complicated logic is located. The class's
134 state is used to move between different states and detect things
135 properly.
136
137 We can be in one of two modes:
138 self.is_log == True: This is 'git log' mode, where most output is
139 indented by 4 characters and we are scanning for tags
140
141 self.is_log == False: This is 'patch' mode, where we already have
142 all the tags, and are processing patches to remove junk we
143 don't want, and add things we think are required.
144
145 Args:
146 line: text line to process
147
148 Returns:
149 list of output lines, or [] if nothing should be output
150 """
151 # Initially we have no output. Prepare the input line string
152 out = []
153 line = line.rstrip('\n')
Scott Wood616a0ab2014-09-25 14:30:46 -0500154
155 commit_match = re_commit.match(line) if self.is_log else None
156
Simon Glass26132882012-01-14 15:12:45 +0000157 if self.is_log:
158 if line[:4] == ' ':
159 line = line[4:]
160
161 # Handle state transition and skipping blank lines
Albert ARIBAUDd880efd2013-11-12 11:14:41 +0100162 series_tag_match = re_series_tag.match(line)
Douglas Anderson52b5ee82019-09-27 09:23:56 -0700163 change_id_match = re_change_id.match(line)
Albert ARIBAUDd880efd2013-11-12 11:14:41 +0100164 commit_tag_match = re_commit_tag.match(line)
Bin Mengd0bc03c2016-06-26 23:24:28 -0700165 cover_match = re_cover.match(line)
Simon Glassc72f3da2013-03-20 16:43:00 +0000166 cover_cc_match = re_cover_cc.match(line)
Simon Glass46b34212014-04-20 10:50:14 -0600167 signoff_match = re_signoff.match(line)
Simon Glass26132882012-01-14 15:12:45 +0000168 tag_match = None
169 if self.state == STATE_PATCH_HEADER:
170 tag_match = re_tag.match(line)
171 is_blank = not line.strip()
172 if is_blank:
173 if (self.state == STATE_MSG_HEADER
174 or self.state == STATE_PATCH_SUBJECT):
175 self.state += 1
176
177 # We don't have a subject in the text stream of patch files
178 # It has its own line with a Subject: tag
179 if not self.is_log and self.state == STATE_PATCH_SUBJECT:
180 self.state += 1
181 elif commit_match:
182 self.state = STATE_MSG_HEADER
183
Bin Meng9228cc22016-06-26 23:24:32 -0700184 # If a tag is detected, or a new commit starts
Douglas Anderson52b5ee82019-09-27 09:23:56 -0700185 if series_tag_match or commit_tag_match or change_id_match or \
Bin Meng9228cc22016-06-26 23:24:32 -0700186 cover_match or cover_cc_match or signoff_match or \
187 self.state == STATE_MSG_HEADER:
Bin Meng50ffc982016-06-26 23:24:31 -0700188 # but we are already in a section, this means 'END' is missing
189 # for that section, fix it up.
Bin Mengf83649f2016-06-26 23:24:29 -0700190 if self.in_section:
191 self.warn.append("Missing 'END' in section '%s'" % self.in_section)
192 if self.in_section == 'cover':
193 self.series.cover = self.section
194 elif self.in_section == 'notes':
195 if self.is_log:
196 self.series.notes += self.section
197 elif self.in_section == 'commit-notes':
198 if self.is_log:
199 self.commit.notes += self.section
200 else:
201 self.warn.append("Unknown section '%s'" % self.in_section)
202 self.in_section = None
203 self.skip_blank = True
204 self.section = []
Bin Meng50ffc982016-06-26 23:24:31 -0700205 # but we are already in a change list, that means a blank line
206 # is missing, fix it up.
207 if self.in_change:
208 self.warn.append("Missing 'blank line' in section 'Series-changes'")
209 self.in_change = 0
Bin Mengf83649f2016-06-26 23:24:29 -0700210
Simon Glass26132882012-01-14 15:12:45 +0000211 # If we are in a section, keep collecting lines until we see END
212 if self.in_section:
213 if line == 'END':
214 if self.in_section == 'cover':
215 self.series.cover = self.section
216 elif self.in_section == 'notes':
217 if self.is_log:
218 self.series.notes += self.section
Albert ARIBAUDd880efd2013-11-12 11:14:41 +0100219 elif self.in_section == 'commit-notes':
220 if self.is_log:
221 self.commit.notes += self.section
Simon Glass26132882012-01-14 15:12:45 +0000222 else:
223 self.warn.append("Unknown section '%s'" % self.in_section)
224 self.in_section = None
225 self.skip_blank = True
226 self.section = []
227 else:
228 self.section.append(line)
229
230 # Detect the commit subject
231 elif not is_blank and self.state == STATE_PATCH_SUBJECT:
232 self.commit.subject = line
233
234 # Detect the tags we want to remove, and skip blank lines
Albert ARIBAUDd880efd2013-11-12 11:14:41 +0100235 elif re_remove.match(line) and not commit_tag_match:
Simon Glass26132882012-01-14 15:12:45 +0000236 self.skip_blank = True
237
238 # TEST= should be the last thing in the commit, so remove
239 # everything after it
240 if line.startswith('TEST='):
241 self.found_test = True
242 elif self.skip_blank and is_blank:
243 self.skip_blank = False
244
245 # Detect the start of a cover letter section
Bin Mengd0bc03c2016-06-26 23:24:28 -0700246 elif cover_match:
Simon Glass26132882012-01-14 15:12:45 +0000247 self.in_section = 'cover'
248 self.skip_blank = False
249
Simon Glassc72f3da2013-03-20 16:43:00 +0000250 elif cover_cc_match:
251 value = cover_cc_match.group(1)
252 self.AddToSeries(line, 'cover-cc', value)
253
Simon Glass26132882012-01-14 15:12:45 +0000254 # If we are in a change list, key collected lines until a blank one
255 elif self.in_change:
256 if is_blank:
257 # Blank line ends this change list
258 self.in_change = 0
Simon Glass46b34212014-04-20 10:50:14 -0600259 elif line == '---':
Ilya Yanok644c1842012-08-06 23:46:05 +0000260 self.in_change = 0
261 out = self.ProcessLine(line)
Simon Glass26132882012-01-14 15:12:45 +0000262 else:
Ilya Yanokcedee802012-08-06 23:46:06 +0000263 if self.is_log:
264 self.series.AddChange(self.in_change, self.commit, line)
Simon Glass26132882012-01-14 15:12:45 +0000265 self.skip_blank = False
266
267 # Detect Series-xxx tags
Albert ARIBAUDd880efd2013-11-12 11:14:41 +0100268 elif series_tag_match:
269 name = series_tag_match.group(1)
270 value = series_tag_match.group(2)
Simon Glass26132882012-01-14 15:12:45 +0000271 if name == 'changes':
272 # value is the version number: e.g. 1, or 2
273 try:
274 value = int(value)
275 except ValueError as str:
276 raise ValueError("%s: Cannot decode version info '%s'" %
277 (self.commit.hash, line))
278 self.in_change = int(value)
279 else:
280 self.AddToSeries(line, name, value)
281 self.skip_blank = True
282
Douglas Anderson52b5ee82019-09-27 09:23:56 -0700283 # Detect Change-Id tags
284 elif change_id_match:
285 value = change_id_match.group(1)
286 if self.is_log:
287 if self.commit.change_id:
288 raise ValueError("%s: Two Change-Ids: '%s' vs. '%s'" %
289 (self.commit.hash, self.commit.change_id, value))
290 self.commit.change_id = value
291 self.skip_blank = True
292
Albert ARIBAUDd880efd2013-11-12 11:14:41 +0100293 # Detect Commit-xxx tags
294 elif commit_tag_match:
295 name = commit_tag_match.group(1)
296 value = commit_tag_match.group(2)
297 if name == 'notes':
298 self.AddToCommit(line, name, value)
299 self.skip_blank = True
300
Simon Glass26132882012-01-14 15:12:45 +0000301 # Detect the start of a new commit
302 elif commit_match:
303 self.CloseCommit()
Simon Glassbaec6842014-10-15 02:27:00 -0600304 self.commit = commit.Commit(commit_match.group(1))
Simon Glass26132882012-01-14 15:12:45 +0000305
306 # Detect tags in the commit message
307 elif tag_match:
Simon Glass26132882012-01-14 15:12:45 +0000308 # Remove Tested-by self, since few will take much notice
Ilya Yanok98c5e792012-08-06 23:46:08 +0000309 if (tag_match.group(1) == 'Tested-by' and
Simon Glass26132882012-01-14 15:12:45 +0000310 tag_match.group(2).find(os.getenv('USER') + '@') != -1):
311 self.warn.append("Ignoring %s" % line)
Simon Glassf7f01992014-02-16 08:23:47 -0700312 elif tag_match.group(1) == 'Patch-cc':
Simon Glass26132882012-01-14 15:12:45 +0000313 self.commit.AddCc(tag_match.group(2).split(','))
314 else:
Simon Glass27374d32014-08-28 09:43:38 -0600315 out = [line]
Simon Glass26132882012-01-14 15:12:45 +0000316
Simon Glass46b34212014-04-20 10:50:14 -0600317 # Suppress duplicate signoffs
318 elif signoff_match:
Simon Glassb0cd3412014-08-28 09:43:35 -0600319 if (self.is_log or not self.commit or
Simon Glass88872ea2014-05-13 12:14:02 -0600320 self.commit.CheckDuplicateSignoff(signoff_match.group(1))):
Simon Glass46b34212014-04-20 10:50:14 -0600321 out = [line]
322
Simon Glass26132882012-01-14 15:12:45 +0000323 # Well that means this is an ordinary line
324 else:
Simon Glass26132882012-01-14 15:12:45 +0000325 # Look for space before tab
326 m = re_space_before_tab.match(line)
327 if m:
328 self.warn.append('Line %d/%d has space before tab' %
329 (self.linenum, m.start()))
330
331 # OK, we have a valid non-blank line
332 out = [line]
333 self.linenum += 1
334 self.skip_blank = False
335 if self.state == STATE_DIFFS:
336 pass
337
338 # If this is the start of the diffs section, emit our tags and
339 # change log
340 elif line == '---':
341 self.state = STATE_DIFFS
342
343 # Output the tags (signeoff first), then change list
344 out = []
Simon Glass26132882012-01-14 15:12:45 +0000345 log = self.series.MakeChangeLog(self.commit)
Simon Glassb0cd3412014-08-28 09:43:35 -0600346 out += [line]
347 if self.commit:
348 out += self.commit.notes
349 out += [''] + log
Simon Glass26132882012-01-14 15:12:45 +0000350 elif self.found_test:
351 if not re_allowed_after_test.match(line):
352 self.lines_after_test += 1
353
354 return out
355
356 def Finalize(self):
357 """Close out processing of this patch stream"""
358 self.CloseCommit()
359 if self.lines_after_test:
360 self.warn.append('Found %d lines after TEST=' %
361 self.lines_after_test)
362
Douglas Anderson52b5ee82019-09-27 09:23:56 -0700363 def WriteMessageId(self, outfd):
364 """Write the Message-Id into the output.
365
366 This is based on the Change-Id in the original patch, the version,
367 and the prefix.
368
369 Args:
370 outfd: Output stream file object
371 """
372 if not self.commit.change_id:
373 return
374
375 # If the count is -1 we're testing, so use a fixed time
376 if self.commit.count == -1:
377 time_now = datetime.datetime(1999, 12, 31, 23, 59, 59)
378 else:
379 time_now = datetime.datetime.now()
380
381 # In theory there is email.utils.make_msgid() which would be nice
382 # to use, but it already produces something way too long and thus
383 # will produce ugly commit lines if someone throws this into
384 # a "Link:" tag in the final commit. So (sigh) roll our own.
385
386 # Start with the time; presumably we wouldn't send the same series
387 # with the same Change-Id at the exact same second.
388 parts = [time_now.strftime("%Y%m%d%H%M%S")]
389
390 # These seem like they would be nice to include.
391 if 'prefix' in self.series:
392 parts.append(self.series['prefix'])
393 if 'version' in self.series:
394 parts.append("v%s" % self.series['version'])
395
396 parts.append(str(self.commit.count + 1))
397
398 # The Change-Id must be last, right before the @
399 parts.append(self.commit.change_id)
400
401 # Join parts together with "." and write it out.
402 outfd.write('Message-Id: <%s@changeid>\n' % '.'.join(parts))
403
Simon Glass26132882012-01-14 15:12:45 +0000404 def ProcessStream(self, infd, outfd):
405 """Copy a stream from infd to outfd, filtering out unwanting things.
406
407 This is used to process patch files one at a time.
408
409 Args:
410 infd: Input stream file object
411 outfd: Output stream file object
412 """
413 # Extract the filename from each diff, for nice warnings
414 fname = None
415 last_fname = None
416 re_fname = re.compile('diff --git a/(.*) b/.*')
Douglas Anderson52b5ee82019-09-27 09:23:56 -0700417
418 self.WriteMessageId(outfd)
419
Simon Glass26132882012-01-14 15:12:45 +0000420 while True:
421 line = infd.readline()
422 if not line:
423 break
424 out = self.ProcessLine(line)
425
426 # Try to detect blank lines at EOF
427 for line in out:
428 match = re_fname.match(line)
429 if match:
430 last_fname = fname
431 fname = match.group(1)
432 if line == '+':
433 self.blank_count += 1
434 else:
435 if self.blank_count and (line == '-- ' or match):
436 self.warn.append("Found possible blank line(s) at "
437 "end of file '%s'" % last_fname)
438 outfd.write('+\n' * self.blank_count)
439 outfd.write(line + '\n')
440 self.blank_count = 0
441 self.Finalize()
442
443
Simon Glass680da3c2012-12-15 10:42:06 +0000444def GetMetaDataForList(commit_range, git_dir=None, count=None,
Simon Glass359b55a62014-09-05 19:00:23 -0600445 series = None, allow_overwrite=False):
Simon Glass26132882012-01-14 15:12:45 +0000446 """Reads out patch series metadata from the commits
447
448 This does a 'git log' on the relevant commits and pulls out the tags we
449 are interested in.
450
451 Args:
Simon Glass680da3c2012-12-15 10:42:06 +0000452 commit_range: Range of commits to count (e.g. 'HEAD..base')
453 git_dir: Path to git repositiory (None to use default)
454 count: Number of commits to list, or None for no limit
455 series: Series object to add information into. By default a new series
456 is started.
Simon Glass359b55a62014-09-05 19:00:23 -0600457 allow_overwrite: Allow tags to overwrite an existing tag
Simon Glass680da3c2012-12-15 10:42:06 +0000458 Returns:
459 A Series object containing information about the commits.
Simon Glass26132882012-01-14 15:12:45 +0000460 """
Simon Glass4e5d6bc2014-09-05 19:00:19 -0600461 if not series:
462 series = Series()
Simon Glass359b55a62014-09-05 19:00:23 -0600463 series.allow_overwrite = allow_overwrite
Simon Glassfc372bc2016-03-06 19:45:33 -0700464 params = gitutil.LogCmd(commit_range, reverse=True, count=count,
Simon Glassb9dbcb42014-08-09 15:33:10 -0600465 git_dir=git_dir)
466 stdout = command.RunPipe([params], capture=True).stdout
Simon Glass26132882012-01-14 15:12:45 +0000467 ps = PatchStream(series, is_log=True)
468 for line in stdout.splitlines():
469 ps.ProcessLine(line)
470 ps.Finalize()
471 return series
472
Simon Glass680da3c2012-12-15 10:42:06 +0000473def GetMetaData(start, count):
474 """Reads out patch series metadata from the commits
475
476 This does a 'git log' on the relevant commits and pulls out the tags we
477 are interested in.
478
479 Args:
480 start: Commit to start from: 0=HEAD, 1=next one, etc.
481 count: Number of commits to list
482 """
483 return GetMetaDataForList('HEAD~%d' % start, None, count)
484
Simon Glassdf1bc5c2017-05-29 15:31:31 -0600485def GetMetaDataForTest(text):
486 """Process metadata from a file containing a git log. Used for tests
487
488 Args:
489 text:
490 """
491 series = Series()
492 ps = PatchStream(series, is_log=True)
493 for line in text.splitlines():
494 ps.ProcessLine(line)
495 ps.Finalize()
496 return series
497
Simon Glass26132882012-01-14 15:12:45 +0000498def FixPatch(backup_dir, fname, series, commit):
499 """Fix up a patch file, by adding/removing as required.
500
501 We remove our tags from the patch file, insert changes lists, etc.
502 The patch file is processed in place, and overwritten.
503
504 A backup file is put into backup_dir (if not None).
505
506 Args:
507 fname: Filename to patch file to process
508 series: Series information about this patch set
509 commit: Commit object for this patch file
510 Return:
511 A list of errors, or [] if all ok.
512 """
513 handle, tmpname = tempfile.mkstemp()
Simon Glassf544a2d2019-10-31 07:42:51 -0600514 outfd = os.fdopen(handle, 'w', encoding='utf-8')
515 infd = open(fname, 'r', encoding='utf-8')
Simon Glass26132882012-01-14 15:12:45 +0000516 ps = PatchStream(series)
517 ps.commit = commit
518 ps.ProcessStream(infd, outfd)
519 infd.close()
520 outfd.close()
521
522 # Create a backup file if required
523 if backup_dir:
524 shutil.copy(fname, os.path.join(backup_dir, os.path.basename(fname)))
525 shutil.move(tmpname, fname)
526 return ps.warn
527
528def FixPatches(series, fnames):
529 """Fix up a list of patches identified by filenames
530
531 The patch files are processed in place, and overwritten.
532
533 Args:
534 series: The series object
535 fnames: List of patch files to process
536 """
537 # Current workflow creates patches, so we shouldn't need a backup
538 backup_dir = None #tempfile.mkdtemp('clean-patch')
539 count = 0
540 for fname in fnames:
541 commit = series.commits[count]
542 commit.patch = fname
Douglas Anderson52b5ee82019-09-27 09:23:56 -0700543 commit.count = count
Simon Glass26132882012-01-14 15:12:45 +0000544 result = FixPatch(backup_dir, fname, series, commit)
545 if result:
Paul Burtonc3931342016-09-27 16:03:50 +0100546 print('%d warnings for %s:' % (len(result), fname))
Simon Glass26132882012-01-14 15:12:45 +0000547 for warn in result:
Paul Burtonc3931342016-09-27 16:03:50 +0100548 print('\t', warn)
Simon Glass26132882012-01-14 15:12:45 +0000549 print
550 count += 1
Paul Burtonc3931342016-09-27 16:03:50 +0100551 print('Cleaned %d patches' % count)
Simon Glass26132882012-01-14 15:12:45 +0000552
553def InsertCoverLetter(fname, series, count):
554 """Inserts a cover letter with the required info into patch 0
555
556 Args:
557 fname: Input / output filename of the cover letter file
558 series: Series object
559 count: Number of patches in the series
560 """
561 fd = open(fname, 'r')
562 lines = fd.readlines()
563 fd.close()
564
565 fd = open(fname, 'w')
566 text = series.cover
567 prefix = series.GetPatchPrefix()
568 for line in lines:
569 if line.startswith('Subject:'):
Wu, Josh393aaa22015-04-03 10:51:17 +0800570 # if more than 10 or 100 patches, it should say 00/xx, 000/xxx, etc
571 zero_repeat = int(math.log10(count)) + 1
572 zero = '0' * zero_repeat
573 line = 'Subject: [%s %s/%d] %s\n' % (prefix, zero, count, text[0])
Simon Glass26132882012-01-14 15:12:45 +0000574
575 # Insert our cover letter
576 elif line.startswith('*** BLURB HERE ***'):
577 # First the blurb test
578 line = '\n'.join(text[1:]) + '\n'
579 if series.get('notes'):
580 line += '\n'.join(series.notes) + '\n'
581
582 # Now the change list
583 out = series.MakeChangeLog(None)
584 line += '\n' + '\n'.join(out)
585 fd.write(line)
586 fd.close()