diff options
Diffstat (limited to 'tools/patman/series.py')
| -rw-r--r-- | tools/patman/series.py | 238 | 
1 files changed, 238 insertions, 0 deletions
| diff --git a/tools/patman/series.py b/tools/patman/series.py new file mode 100644 index 000000000..05d9e73a4 --- /dev/null +++ b/tools/patman/series.py @@ -0,0 +1,238 @@ +# Copyright (c) 2011 The Chromium OS Authors. +# +# See file CREDITS for list of people who contributed to this +# project. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, +# MA 02111-1307 USA +# + +import os + +import gitutil +import terminal + +# Series-xxx tags that we understand +valid_series = ['to', 'cc', 'version', 'changes', 'prefix', 'notes']; + +class Series(dict): +    """Holds information about a patch series, including all tags. + +    Vars: +        cc: List of aliases/emails to Cc all patches to +        commits: List of Commit objects, one for each patch +        cover: List of lines in the cover letter +        notes: List of lines in the notes +        changes: (dict) List of changes for each version, The key is +            the integer version number +    """ +    def __init__(self): +        self.cc = [] +        self.to = [] +        self.commits = [] +        self.cover = None +        self.notes = [] +        self.changes = {} + +    # These make us more like a dictionary +    def __setattr__(self, name, value): +        self[name] = value + +    def __getattr__(self, name): +        return self[name] + +    def AddTag(self, commit, line, name, value): +        """Add a new Series-xxx tag along with its value. + +        Args: +            line: Source line containing tag (useful for debug/error messages) +            name: Tag name (part after 'Series-') +            value: Tag value (part after 'Series-xxx: ') +        """ +        # If we already have it, then add to our list +        if name in self: +            values = value.split(',') +            values = [str.strip() for str in values] +            if type(self[name]) != type([]): +                raise ValueError("In %s: line '%s': Cannot add another value " +                        "'%s' to series '%s'" % +                            (commit.hash, line, values, self[name])) +            self[name] += values + +        # Otherwise just set the value +        elif name in valid_series: +            self[name] = value +        else: +            raise ValueError("In %s: line '%s': Unknown 'Series-%s': valid " +                        "options are %s" % (self.commit.hash, line, name, +                            ', '.join(valid_series))) + +    def AddCommit(self, commit): +        """Add a commit into our list of commits + +        We create a list of tags in the commit subject also. + +        Args: +            commit: Commit object to add +        """ +        commit.CheckTags() +        self.commits.append(commit) + +    def ShowActions(self, args, cmd, process_tags): +        """Show what actions we will/would perform + +        Args: +            args: List of patch files we created +            cmd: The git command we would have run +            process_tags: Process tags as if they were aliases +        """ +        col = terminal.Color() +        print 'Dry run, so not doing much. But I would do this:' +        print +        print 'Send a total of %d patch%s with %scover letter.' % ( +                len(args), '' if len(args) == 1 else 'es', +                self.get('cover') and 'a ' or 'no ') + +        # TODO: Colour the patches according to whether they passed checks +        for upto in range(len(args)): +            commit = self.commits[upto] +            print col.Color(col.GREEN, '   %s' % args[upto]) +            cc_list = [] +            if process_tags: +                cc_list += gitutil.BuildEmailList(commit.tags) +            cc_list += gitutil.BuildEmailList(commit.cc_list) + +            for email in cc_list: +                if email == None: +                    email = col.Color(col.YELLOW, "<alias '%s' not found>" +                            % tag) +                if email: +                    print '      Cc: ',email +        print +        for item in gitutil.BuildEmailList(self.get('to', '<none>')): +            print 'To:\t ', item +        for item in gitutil.BuildEmailList(self.cc): +            print 'Cc:\t ', item +        print 'Version: ', self.get('version') +        print 'Prefix:\t ', self.get('prefix') +        if self.cover: +            print 'Cover: %d lines' % len(self.cover) +        if cmd: +            print 'Git command: %s' % cmd + +    def MakeChangeLog(self, commit): +        """Create a list of changes for each version. + +        Return: +            The change log as a list of strings, one per line + +            Changes in v1: +            - Fix the widget +            - Jog the dial + +            Changes in v2: +            - Jog the dial back closer to the widget + +            etc. +        """ +        final = [] +        need_blank = False +        for change in sorted(self.changes): +            out = [] +            for this_commit, text in self.changes[change]: +                if commit and this_commit != commit: +                    continue +                if text not in out: +                    out.append(text) +            if out: +                out = ['Changes in v%d:' % change] + sorted(out) +                if need_blank: +                    out = [''] + out +                final += out +                need_blank = True +        if self.changes: +            final.append('') +        return final + +    def DoChecks(self): +        """Check that each version has a change log + +        Print an error if something is wrong. +        """ +        col = terminal.Color() +        if self.get('version'): +            changes_copy = dict(self.changes) +            for version in range(2, int(self.version) + 1): +                if self.changes.get(version): +                    del changes_copy[version] +                else: +                    str = 'Change log missing for v%d' % version +                    print col.Color(col.RED, str) +            for version in changes_copy: +                str = 'Change log for unknown version v%d' % version +                print col.Color(col.RED, str) +        elif self.changes: +            str = 'Change log exists, but no version is set' +            print col.Color(col.RED, str) + +    def MakeCcFile(self, process_tags): +        """Make a cc file for us to use for per-commit Cc automation + +        Args: +            process_tags: Process tags as if they were aliases +        Return: +            Filename of temp file created +        """ +        # Look for commit tags (of the form 'xxx:' at the start of the subject) +        fname = '/tmp/patman.%d' % os.getpid() +        fd = open(fname, 'w') +        for commit in self.commits: +            list = [] +            if process_tags: +                list += gitutil.BuildEmailList(commit.tags) +            list += gitutil.BuildEmailList(commit.cc_list) +            print >>fd, commit.patch, ', '.join(list) + +        fd.close() +        return fname + +    def AddChange(self, version, commit, info): +        """Add a new change line to a version. + +        This will later appear in the change log. + +        Args: +            version: version number to add change list to +            info: change line for this version +        """ +        if not self.changes.get(version): +            self.changes[version] = [] +        self.changes[version].append([commit, info]) + +    def GetPatchPrefix(self): +        """Get the patch version string + +        Return: +            Patch string, like 'RFC PATCH v5' or just 'PATCH' +        """ +        version = '' +        if self.get('version'): +            version = ' v%s' % self['version'] + +        # Get patch name prefix +        prefix = '' +        if self.get('prefix'): +            prefix = '%s ' % self['prefix'] +        return '%sPATCH%s' % (prefix, version) |