xref: /rk3399_rockchip-uboot/tools/patman/patchstream.py (revision 7f14f30a6d6c80f9fbe3fd8d5b0c294575e3094e)
10d24de9dSSimon Glass# Copyright (c) 2011 The Chromium OS Authors.
20d24de9dSSimon Glass#
30d24de9dSSimon Glass# See file CREDITS for list of people who contributed to this
40d24de9dSSimon Glass# project.
50d24de9dSSimon Glass#
60d24de9dSSimon Glass# This program is free software; you can redistribute it and/or
70d24de9dSSimon Glass# modify it under the terms of the GNU General Public License as
80d24de9dSSimon Glass# published by the Free Software Foundation; either version 2 of
90d24de9dSSimon Glass# the License, or (at your option) any later version.
100d24de9dSSimon Glass#
110d24de9dSSimon Glass# This program is distributed in the hope that it will be useful,
120d24de9dSSimon Glass# but WITHOUT ANY WARRANTY; without even the implied warranty of
130d24de9dSSimon Glass# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
140d24de9dSSimon Glass# GNU General Public License for more details.
150d24de9dSSimon Glass#
160d24de9dSSimon Glass# You should have received a copy of the GNU General Public License
170d24de9dSSimon Glass# along with this program; if not, write to the Free Software
180d24de9dSSimon Glass# Foundation, Inc., 59 Temple Place, Suite 330, Boston,
190d24de9dSSimon Glass# MA 02111-1307 USA
200d24de9dSSimon Glass#
210d24de9dSSimon Glass
220d24de9dSSimon Glassimport os
230d24de9dSSimon Glassimport re
240d24de9dSSimon Glassimport shutil
250d24de9dSSimon Glassimport tempfile
260d24de9dSSimon Glass
270d24de9dSSimon Glassimport command
280d24de9dSSimon Glassimport commit
290d24de9dSSimon Glassimport gitutil
300d24de9dSSimon Glassfrom series import Series
310d24de9dSSimon Glass
320d24de9dSSimon Glass# Tags that we detect and remove
330d24de9dSSimon Glassre_remove = re.compile('^BUG=|^TEST=|^Change-Id:|^Review URL:'
340d24de9dSSimon Glass    '|Reviewed-on:|Reviewed-by:')
350d24de9dSSimon Glass
360d24de9dSSimon Glass# Lines which are allowed after a TEST= line
370d24de9dSSimon Glassre_allowed_after_test = re.compile('^Signed-off-by:')
380d24de9dSSimon Glass
3905e5b735SIlya Yanok# Signoffs
4005e5b735SIlya Yanokre_signoff = re.compile('^Signed-off-by:')
4105e5b735SIlya Yanok
420d24de9dSSimon Glass# The start of the cover letter
430d24de9dSSimon Glassre_cover = re.compile('^Cover-letter:')
440d24de9dSSimon Glass
450d24de9dSSimon Glass# Patch series tag
460d24de9dSSimon Glassre_series = re.compile('^Series-(\w*): *(.*)')
470d24de9dSSimon Glass
480d24de9dSSimon Glass# Commit tags that we want to collect and keep
49c7379149SIlya Yanokre_tag = re.compile('^(Tested-by|Acked-by|Cc): (.*)')
500d24de9dSSimon Glass
510d24de9dSSimon Glass# The start of a new commit in the git log
520d24de9dSSimon Glassre_commit = re.compile('^commit (.*)')
530d24de9dSSimon Glass
540d24de9dSSimon Glass# We detect these since checkpatch doesn't always do it
550d24de9dSSimon Glassre_space_before_tab = re.compile('^[+].* \t')
560d24de9dSSimon Glass
570d24de9dSSimon Glass# States we can be in - can we use range() and still have comments?
580d24de9dSSimon GlassSTATE_MSG_HEADER = 0        # Still in the message header
590d24de9dSSimon GlassSTATE_PATCH_SUBJECT = 1     # In patch subject (first line of log for a commit)
600d24de9dSSimon GlassSTATE_PATCH_HEADER = 2      # In patch header (after the subject)
610d24de9dSSimon GlassSTATE_DIFFS = 3             # In the diff part (past --- line)
620d24de9dSSimon Glass
630d24de9dSSimon Glassclass PatchStream:
640d24de9dSSimon Glass    """Class for detecting/injecting tags in a patch or series of patches
650d24de9dSSimon Glass
660d24de9dSSimon Glass    We support processing the output of 'git log' to read out the tags we
670d24de9dSSimon Glass    are interested in. We can also process a patch file in order to remove
680d24de9dSSimon Glass    unwanted tags or inject additional ones. These correspond to the two
690d24de9dSSimon Glass    phases of processing.
700d24de9dSSimon Glass    """
710d24de9dSSimon Glass    def __init__(self, series, name=None, is_log=False):
720d24de9dSSimon Glass        self.skip_blank = False          # True to skip a single blank line
730d24de9dSSimon Glass        self.found_test = False          # Found a TEST= line
740d24de9dSSimon Glass        self.lines_after_test = 0        # MNumber of lines found after TEST=
750d24de9dSSimon Glass        self.warn = []                   # List of warnings we have collected
760d24de9dSSimon Glass        self.linenum = 1                 # Output line number we are up to
770d24de9dSSimon Glass        self.in_section = None           # Name of start...END section we are in
780d24de9dSSimon Glass        self.notes = []                  # Series notes
790d24de9dSSimon Glass        self.section = []                # The current section...END section
800d24de9dSSimon Glass        self.series = series             # Info about the patch series
810d24de9dSSimon Glass        self.is_log = is_log             # True if indent like git log
820d24de9dSSimon Glass        self.in_change = 0               # Non-zero if we are in a change list
830d24de9dSSimon Glass        self.blank_count = 0             # Number of blank lines stored up
840d24de9dSSimon Glass        self.state = STATE_MSG_HEADER    # What state are we in?
850d24de9dSSimon Glass        self.tags = []                   # Tags collected, like Tested-by...
860d24de9dSSimon Glass        self.signoff = []                # Contents of signoff line
870d24de9dSSimon Glass        self.commit = None               # Current commit
880d24de9dSSimon Glass
890d24de9dSSimon Glass    def AddToSeries(self, line, name, value):
900d24de9dSSimon Glass        """Add a new Series-xxx tag.
910d24de9dSSimon Glass
920d24de9dSSimon Glass        When a Series-xxx tag is detected, we come here to record it, if we
930d24de9dSSimon Glass        are scanning a 'git log'.
940d24de9dSSimon Glass
950d24de9dSSimon Glass        Args:
960d24de9dSSimon Glass            line: Source line containing tag (useful for debug/error messages)
970d24de9dSSimon Glass            name: Tag name (part after 'Series-')
980d24de9dSSimon Glass            value: Tag value (part after 'Series-xxx: ')
990d24de9dSSimon Glass        """
1000d24de9dSSimon Glass        if name == 'notes':
1010d24de9dSSimon Glass            self.in_section = name
1020d24de9dSSimon Glass            self.skip_blank = False
1030d24de9dSSimon Glass        if self.is_log:
1040d24de9dSSimon Glass            self.series.AddTag(self.commit, line, name, value)
1050d24de9dSSimon Glass
1060d24de9dSSimon Glass    def CloseCommit(self):
1070d24de9dSSimon Glass        """Save the current commit into our commit list, and reset our state"""
1080d24de9dSSimon Glass        if self.commit and self.is_log:
1090d24de9dSSimon Glass            self.series.AddCommit(self.commit)
1100d24de9dSSimon Glass            self.commit = None
1110d24de9dSSimon Glass
1120d24de9dSSimon Glass    def FormatTags(self, tags):
1130d24de9dSSimon Glass        out_list = []
1140d24de9dSSimon Glass        for tag in sorted(tags):
1150d24de9dSSimon Glass            if tag.startswith('Cc:'):
1160d24de9dSSimon Glass                tag_list = tag[4:].split(',')
1170d24de9dSSimon Glass                out_list += gitutil.BuildEmailList(tag_list, 'Cc:')
1180d24de9dSSimon Glass            else:
1190d24de9dSSimon Glass                out_list.append(tag)
1200d24de9dSSimon Glass        return out_list
1210d24de9dSSimon Glass
1220d24de9dSSimon Glass    def ProcessLine(self, line):
1230d24de9dSSimon Glass        """Process a single line of a patch file or commit log
1240d24de9dSSimon Glass
1250d24de9dSSimon Glass        This process a line and returns a list of lines to output. The list
1260d24de9dSSimon Glass        may be empty or may contain multiple output lines.
1270d24de9dSSimon Glass
1280d24de9dSSimon Glass        This is where all the complicated logic is located. The class's
1290d24de9dSSimon Glass        state is used to move between different states and detect things
1300d24de9dSSimon Glass        properly.
1310d24de9dSSimon Glass
1320d24de9dSSimon Glass        We can be in one of two modes:
1330d24de9dSSimon Glass            self.is_log == True: This is 'git log' mode, where most output is
1340d24de9dSSimon Glass                indented by 4 characters and we are scanning for tags
1350d24de9dSSimon Glass
1360d24de9dSSimon Glass            self.is_log == False: This is 'patch' mode, where we already have
1370d24de9dSSimon Glass                all the tags, and are processing patches to remove junk we
1380d24de9dSSimon Glass                don't want, and add things we think are required.
1390d24de9dSSimon Glass
1400d24de9dSSimon Glass        Args:
1410d24de9dSSimon Glass            line: text line to process
1420d24de9dSSimon Glass
1430d24de9dSSimon Glass        Returns:
1440d24de9dSSimon Glass            list of output lines, or [] if nothing should be output
1450d24de9dSSimon Glass        """
1460d24de9dSSimon Glass        # Initially we have no output. Prepare the input line string
1470d24de9dSSimon Glass        out = []
1480d24de9dSSimon Glass        line = line.rstrip('\n')
1490d24de9dSSimon Glass        if self.is_log:
1500d24de9dSSimon Glass            if line[:4] == '    ':
1510d24de9dSSimon Glass                line = line[4:]
1520d24de9dSSimon Glass
1530d24de9dSSimon Glass        # Handle state transition and skipping blank lines
1540d24de9dSSimon Glass        series_match = re_series.match(line)
1550d24de9dSSimon Glass        commit_match = re_commit.match(line) if self.is_log else None
1560d24de9dSSimon Glass        tag_match = None
1570d24de9dSSimon Glass        if self.state == STATE_PATCH_HEADER:
1580d24de9dSSimon Glass            tag_match = re_tag.match(line)
1590d24de9dSSimon Glass        is_blank = not line.strip()
1600d24de9dSSimon Glass        if is_blank:
1610d24de9dSSimon Glass            if (self.state == STATE_MSG_HEADER
1620d24de9dSSimon Glass                    or self.state == STATE_PATCH_SUBJECT):
1630d24de9dSSimon Glass                self.state += 1
1640d24de9dSSimon Glass
1650d24de9dSSimon Glass            # We don't have a subject in the text stream of patch files
1660d24de9dSSimon Glass            # It has its own line with a Subject: tag
1670d24de9dSSimon Glass            if not self.is_log and self.state == STATE_PATCH_SUBJECT:
1680d24de9dSSimon Glass                self.state += 1
1690d24de9dSSimon Glass        elif commit_match:
1700d24de9dSSimon Glass            self.state = STATE_MSG_HEADER
1710d24de9dSSimon Glass
1720d24de9dSSimon Glass        # If we are in a section, keep collecting lines until we see END
1730d24de9dSSimon Glass        if self.in_section:
1740d24de9dSSimon Glass            if line == 'END':
1750d24de9dSSimon Glass                if self.in_section == 'cover':
1760d24de9dSSimon Glass                    self.series.cover = self.section
1770d24de9dSSimon Glass                elif self.in_section == 'notes':
1780d24de9dSSimon Glass                    if self.is_log:
1790d24de9dSSimon Glass                        self.series.notes += self.section
1800d24de9dSSimon Glass                else:
1810d24de9dSSimon Glass                    self.warn.append("Unknown section '%s'" % self.in_section)
1820d24de9dSSimon Glass                self.in_section = None
1830d24de9dSSimon Glass                self.skip_blank = True
1840d24de9dSSimon Glass                self.section = []
1850d24de9dSSimon Glass            else:
1860d24de9dSSimon Glass                self.section.append(line)
1870d24de9dSSimon Glass
1880d24de9dSSimon Glass        # Detect the commit subject
1890d24de9dSSimon Glass        elif not is_blank and self.state == STATE_PATCH_SUBJECT:
1900d24de9dSSimon Glass            self.commit.subject = line
1910d24de9dSSimon Glass
1920d24de9dSSimon Glass        # Detect the tags we want to remove, and skip blank lines
1930d24de9dSSimon Glass        elif re_remove.match(line):
1940d24de9dSSimon Glass            self.skip_blank = True
1950d24de9dSSimon Glass
1960d24de9dSSimon Glass            # TEST= should be the last thing in the commit, so remove
1970d24de9dSSimon Glass            # everything after it
1980d24de9dSSimon Glass            if line.startswith('TEST='):
1990d24de9dSSimon Glass                self.found_test = True
2000d24de9dSSimon Glass        elif self.skip_blank and is_blank:
2010d24de9dSSimon Glass            self.skip_blank = False
2020d24de9dSSimon Glass
2030d24de9dSSimon Glass        # Detect the start of a cover letter section
2040d24de9dSSimon Glass        elif re_cover.match(line):
2050d24de9dSSimon Glass            self.in_section = 'cover'
2060d24de9dSSimon Glass            self.skip_blank = False
2070d24de9dSSimon Glass
2080d24de9dSSimon Glass        # If we are in a change list, key collected lines until a blank one
2090d24de9dSSimon Glass        elif self.in_change:
2100d24de9dSSimon Glass            if is_blank:
2110d24de9dSSimon Glass                # Blank line ends this change list
2120d24de9dSSimon Glass                self.in_change = 0
21305e5b735SIlya Yanok            elif line == '---' or re_signoff.match(line):
21405e5b735SIlya Yanok                self.in_change = 0
21505e5b735SIlya Yanok                out = self.ProcessLine(line)
2160d24de9dSSimon Glass            else:
217a8840cb2SIlya Yanok                if self.is_log:
2180d24de9dSSimon Glass                    self.series.AddChange(self.in_change, self.commit, line)
2190d24de9dSSimon Glass            self.skip_blank = False
2200d24de9dSSimon Glass
2210d24de9dSSimon Glass        # Detect Series-xxx tags
2220d24de9dSSimon Glass        elif series_match:
2230d24de9dSSimon Glass            name = series_match.group(1)
2240d24de9dSSimon Glass            value = series_match.group(2)
2250d24de9dSSimon Glass            if name == 'changes':
2260d24de9dSSimon Glass                # value is the version number: e.g. 1, or 2
2270d24de9dSSimon Glass                try:
2280d24de9dSSimon Glass                    value = int(value)
2290d24de9dSSimon Glass                except ValueError as str:
2300d24de9dSSimon Glass                    raise ValueError("%s: Cannot decode version info '%s'" %
2310d24de9dSSimon Glass                        (self.commit.hash, line))
2320d24de9dSSimon Glass                self.in_change = int(value)
2330d24de9dSSimon Glass            else:
2340d24de9dSSimon Glass                self.AddToSeries(line, name, value)
2350d24de9dSSimon Glass                self.skip_blank = True
2360d24de9dSSimon Glass
2370d24de9dSSimon Glass        # Detect the start of a new commit
2380d24de9dSSimon Glass        elif commit_match:
2390d24de9dSSimon Glass            self.CloseCommit()
2400d24de9dSSimon Glass            self.commit = commit.Commit(commit_match.group(1)[:7])
2410d24de9dSSimon Glass
2420d24de9dSSimon Glass        # Detect tags in the commit message
2430d24de9dSSimon Glass        elif tag_match:
2440d24de9dSSimon Glass            # Remove Tested-by self, since few will take much notice
245c7379149SIlya Yanok            if (tag_match.group(1) == 'Tested-by' and
2460d24de9dSSimon Glass                    tag_match.group(2).find(os.getenv('USER') + '@') != -1):
2470d24de9dSSimon Glass                self.warn.append("Ignoring %s" % line)
2480d24de9dSSimon Glass            elif tag_match.group(1) == 'Cc':
2490d24de9dSSimon Glass                self.commit.AddCc(tag_match.group(2).split(','))
2500d24de9dSSimon Glass            else:
2510d24de9dSSimon Glass                self.tags.append(line);
2520d24de9dSSimon Glass
2530d24de9dSSimon Glass        # Well that means this is an ordinary line
2540d24de9dSSimon Glass        else:
2550d24de9dSSimon Glass            pos = 1
2560d24de9dSSimon Glass            # Look for ugly ASCII characters
2570d24de9dSSimon Glass            for ch in line:
2580d24de9dSSimon Glass                # TODO: Would be nicer to report source filename and line
2590d24de9dSSimon Glass                if ord(ch) > 0x80:
2600d24de9dSSimon Glass                    self.warn.append("Line %d/%d ('%s') has funny ascii char" %
2610d24de9dSSimon Glass                        (self.linenum, pos, line))
2620d24de9dSSimon Glass                pos += 1
2630d24de9dSSimon Glass
2640d24de9dSSimon Glass            # Look for space before tab
2650d24de9dSSimon Glass            m = re_space_before_tab.match(line)
2660d24de9dSSimon Glass            if m:
2670d24de9dSSimon Glass                self.warn.append('Line %d/%d has space before tab' %
2680d24de9dSSimon Glass                    (self.linenum, m.start()))
2690d24de9dSSimon Glass
2700d24de9dSSimon Glass            # OK, we have a valid non-blank line
2710d24de9dSSimon Glass            out = [line]
2720d24de9dSSimon Glass            self.linenum += 1
2730d24de9dSSimon Glass            self.skip_blank = False
2740d24de9dSSimon Glass            if self.state == STATE_DIFFS:
2750d24de9dSSimon Glass                pass
2760d24de9dSSimon Glass
2770d24de9dSSimon Glass            # If this is the start of the diffs section, emit our tags and
2780d24de9dSSimon Glass            # change log
2790d24de9dSSimon Glass            elif line == '---':
2800d24de9dSSimon Glass                self.state = STATE_DIFFS
2810d24de9dSSimon Glass
2820d24de9dSSimon Glass                # Output the tags (signeoff first), then change list
2830d24de9dSSimon Glass                out = []
2840d24de9dSSimon Glass                log = self.series.MakeChangeLog(self.commit)
2850d24de9dSSimon Glass                out += self.FormatTags(self.tags)
2860d24de9dSSimon Glass                out += [line] + log
2870d24de9dSSimon Glass            elif self.found_test:
2880d24de9dSSimon Glass                if not re_allowed_after_test.match(line):
2890d24de9dSSimon Glass                    self.lines_after_test += 1
2900d24de9dSSimon Glass
2910d24de9dSSimon Glass        return out
2920d24de9dSSimon Glass
2930d24de9dSSimon Glass    def Finalize(self):
2940d24de9dSSimon Glass        """Close out processing of this patch stream"""
2950d24de9dSSimon Glass        self.CloseCommit()
2960d24de9dSSimon Glass        if self.lines_after_test:
2970d24de9dSSimon Glass            self.warn.append('Found %d lines after TEST=' %
2980d24de9dSSimon Glass                    self.lines_after_test)
2990d24de9dSSimon Glass
3000d24de9dSSimon Glass    def ProcessStream(self, infd, outfd):
3010d24de9dSSimon Glass        """Copy a stream from infd to outfd, filtering out unwanting things.
3020d24de9dSSimon Glass
3030d24de9dSSimon Glass        This is used to process patch files one at a time.
3040d24de9dSSimon Glass
3050d24de9dSSimon Glass        Args:
3060d24de9dSSimon Glass            infd: Input stream file object
3070d24de9dSSimon Glass            outfd: Output stream file object
3080d24de9dSSimon Glass        """
3090d24de9dSSimon Glass        # Extract the filename from each diff, for nice warnings
3100d24de9dSSimon Glass        fname = None
3110d24de9dSSimon Glass        last_fname = None
3120d24de9dSSimon Glass        re_fname = re.compile('diff --git a/(.*) b/.*')
3130d24de9dSSimon Glass        while True:
3140d24de9dSSimon Glass            line = infd.readline()
3150d24de9dSSimon Glass            if not line:
3160d24de9dSSimon Glass                break
3170d24de9dSSimon Glass            out = self.ProcessLine(line)
3180d24de9dSSimon Glass
3190d24de9dSSimon Glass            # Try to detect blank lines at EOF
3200d24de9dSSimon Glass            for line in out:
3210d24de9dSSimon Glass                match = re_fname.match(line)
3220d24de9dSSimon Glass                if match:
3230d24de9dSSimon Glass                    last_fname = fname
3240d24de9dSSimon Glass                    fname = match.group(1)
3250d24de9dSSimon Glass                if line == '+':
3260d24de9dSSimon Glass                    self.blank_count += 1
3270d24de9dSSimon Glass                else:
3280d24de9dSSimon Glass                    if self.blank_count and (line == '-- ' or match):
3290d24de9dSSimon Glass                        self.warn.append("Found possible blank line(s) at "
3300d24de9dSSimon Glass                                "end of file '%s'" % last_fname)
3310d24de9dSSimon Glass                    outfd.write('+\n' * self.blank_count)
3320d24de9dSSimon Glass                    outfd.write(line + '\n')
3330d24de9dSSimon Glass                    self.blank_count = 0
3340d24de9dSSimon Glass        self.Finalize()
3350d24de9dSSimon Glass
3360d24de9dSSimon Glass
3370d24de9dSSimon Glassdef GetMetaData(start, count):
3380d24de9dSSimon Glass    """Reads out patch series metadata from the commits
3390d24de9dSSimon Glass
3400d24de9dSSimon Glass    This does a 'git log' on the relevant commits and pulls out the tags we
3410d24de9dSSimon Glass    are interested in.
3420d24de9dSSimon Glass
3430d24de9dSSimon Glass    Args:
3440d24de9dSSimon Glass        start: Commit to start from: 0=HEAD, 1=next one, etc.
3450d24de9dSSimon Glass        count: Number of commits to list
3460d24de9dSSimon Glass    """
347*7f14f30aSAlbert ARIBAUD    pipe = [['git', 'log', '--no-color', '--reverse', 'HEAD~%d' % start,
348*7f14f30aSAlbert ARIBAUD	'-n%d' % count]]
3490d24de9dSSimon Glass    stdout = command.RunPipe(pipe, capture=True)
3500d24de9dSSimon Glass    series = Series()
3510d24de9dSSimon Glass    ps = PatchStream(series, is_log=True)
3520d24de9dSSimon Glass    for line in stdout.splitlines():
3530d24de9dSSimon Glass        ps.ProcessLine(line)
3540d24de9dSSimon Glass    ps.Finalize()
3550d24de9dSSimon Glass    return series
3560d24de9dSSimon Glass
3570d24de9dSSimon Glassdef FixPatch(backup_dir, fname, series, commit):
3580d24de9dSSimon Glass    """Fix up a patch file, by adding/removing as required.
3590d24de9dSSimon Glass
3600d24de9dSSimon Glass    We remove our tags from the patch file, insert changes lists, etc.
3610d24de9dSSimon Glass    The patch file is processed in place, and overwritten.
3620d24de9dSSimon Glass
3630d24de9dSSimon Glass    A backup file is put into backup_dir (if not None).
3640d24de9dSSimon Glass
3650d24de9dSSimon Glass    Args:
3660d24de9dSSimon Glass        fname: Filename to patch file to process
3670d24de9dSSimon Glass        series: Series information about this patch set
3680d24de9dSSimon Glass        commit: Commit object for this patch file
3690d24de9dSSimon Glass    Return:
3700d24de9dSSimon Glass        A list of errors, or [] if all ok.
3710d24de9dSSimon Glass    """
3720d24de9dSSimon Glass    handle, tmpname = tempfile.mkstemp()
3730d24de9dSSimon Glass    outfd = os.fdopen(handle, 'w')
3740d24de9dSSimon Glass    infd = open(fname, 'r')
3750d24de9dSSimon Glass    ps = PatchStream(series)
3760d24de9dSSimon Glass    ps.commit = commit
3770d24de9dSSimon Glass    ps.ProcessStream(infd, outfd)
3780d24de9dSSimon Glass    infd.close()
3790d24de9dSSimon Glass    outfd.close()
3800d24de9dSSimon Glass
3810d24de9dSSimon Glass    # Create a backup file if required
3820d24de9dSSimon Glass    if backup_dir:
3830d24de9dSSimon Glass        shutil.copy(fname, os.path.join(backup_dir, os.path.basename(fname)))
3840d24de9dSSimon Glass    shutil.move(tmpname, fname)
3850d24de9dSSimon Glass    return ps.warn
3860d24de9dSSimon Glass
3870d24de9dSSimon Glassdef FixPatches(series, fnames):
3880d24de9dSSimon Glass    """Fix up a list of patches identified by filenames
3890d24de9dSSimon Glass
3900d24de9dSSimon Glass    The patch files are processed in place, and overwritten.
3910d24de9dSSimon Glass
3920d24de9dSSimon Glass    Args:
3930d24de9dSSimon Glass        series: The series object
3940d24de9dSSimon Glass        fnames: List of patch files to process
3950d24de9dSSimon Glass    """
3960d24de9dSSimon Glass    # Current workflow creates patches, so we shouldn't need a backup
3970d24de9dSSimon Glass    backup_dir = None  #tempfile.mkdtemp('clean-patch')
3980d24de9dSSimon Glass    count = 0
3990d24de9dSSimon Glass    for fname in fnames:
4000d24de9dSSimon Glass        commit = series.commits[count]
4010d24de9dSSimon Glass        commit.patch = fname
4020d24de9dSSimon Glass        result = FixPatch(backup_dir, fname, series, commit)
4030d24de9dSSimon Glass        if result:
4040d24de9dSSimon Glass            print '%d warnings for %s:' % (len(result), fname)
4050d24de9dSSimon Glass            for warn in result:
4060d24de9dSSimon Glass                print '\t', warn
4070d24de9dSSimon Glass            print
4080d24de9dSSimon Glass        count += 1
4090d24de9dSSimon Glass    print 'Cleaned %d patches' % count
4100d24de9dSSimon Glass    return series
4110d24de9dSSimon Glass
4120d24de9dSSimon Glassdef InsertCoverLetter(fname, series, count):
4130d24de9dSSimon Glass    """Inserts a cover letter with the required info into patch 0
4140d24de9dSSimon Glass
4150d24de9dSSimon Glass    Args:
4160d24de9dSSimon Glass        fname: Input / output filename of the cover letter file
4170d24de9dSSimon Glass        series: Series object
4180d24de9dSSimon Glass        count: Number of patches in the series
4190d24de9dSSimon Glass    """
4200d24de9dSSimon Glass    fd = open(fname, 'r')
4210d24de9dSSimon Glass    lines = fd.readlines()
4220d24de9dSSimon Glass    fd.close()
4230d24de9dSSimon Glass
4240d24de9dSSimon Glass    fd = open(fname, 'w')
4250d24de9dSSimon Glass    text = series.cover
4260d24de9dSSimon Glass    prefix = series.GetPatchPrefix()
4270d24de9dSSimon Glass    for line in lines:
4280d24de9dSSimon Glass        if line.startswith('Subject:'):
4290d24de9dSSimon Glass            # TODO: if more than 10 patches this should save 00/xx, not 0/xx
4300d24de9dSSimon Glass            line = 'Subject: [%s 0/%d] %s\n' % (prefix, count, text[0])
4310d24de9dSSimon Glass
4320d24de9dSSimon Glass        # Insert our cover letter
4330d24de9dSSimon Glass        elif line.startswith('*** BLURB HERE ***'):
4340d24de9dSSimon Glass            # First the blurb test
4350d24de9dSSimon Glass            line = '\n'.join(text[1:]) + '\n'
4360d24de9dSSimon Glass            if series.get('notes'):
4370d24de9dSSimon Glass                line += '\n'.join(series.notes) + '\n'
4380d24de9dSSimon Glass
4390d24de9dSSimon Glass            # Now the change list
4400d24de9dSSimon Glass            out = series.MakeChangeLog(None)
4410d24de9dSSimon Glass            line += '\n' + '\n'.join(out)
4420d24de9dSSimon Glass        fd.write(line)
4430d24de9dSSimon Glass    fd.close()
444