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 Glass 240d24de9dSSimon Glassimport gitutil 250d24de9dSSimon Glassimport terminal 260d24de9dSSimon Glass 270d24de9dSSimon Glass# Series-xxx tags that we understand 28*ef0e9de8SSimon Glassvalid_series = ['to', 'cc', 'version', 'changes', 'prefix', 'notes', 'name']; 290d24de9dSSimon Glass 300d24de9dSSimon Glassclass Series(dict): 310d24de9dSSimon Glass """Holds information about a patch series, including all tags. 320d24de9dSSimon Glass 330d24de9dSSimon Glass Vars: 340d24de9dSSimon Glass cc: List of aliases/emails to Cc all patches to 350d24de9dSSimon Glass commits: List of Commit objects, one for each patch 360d24de9dSSimon Glass cover: List of lines in the cover letter 370d24de9dSSimon Glass notes: List of lines in the notes 380d24de9dSSimon Glass changes: (dict) List of changes for each version, The key is 390d24de9dSSimon Glass the integer version number 400d24de9dSSimon Glass """ 410d24de9dSSimon Glass def __init__(self): 420d24de9dSSimon Glass self.cc = [] 430d24de9dSSimon Glass self.to = [] 440d24de9dSSimon Glass self.commits = [] 450d24de9dSSimon Glass self.cover = None 460d24de9dSSimon Glass self.notes = [] 470d24de9dSSimon Glass self.changes = {} 480d24de9dSSimon Glass 490d24de9dSSimon Glass # These make us more like a dictionary 500d24de9dSSimon Glass def __setattr__(self, name, value): 510d24de9dSSimon Glass self[name] = value 520d24de9dSSimon Glass 530d24de9dSSimon Glass def __getattr__(self, name): 540d24de9dSSimon Glass return self[name] 550d24de9dSSimon Glass 560d24de9dSSimon Glass def AddTag(self, commit, line, name, value): 570d24de9dSSimon Glass """Add a new Series-xxx tag along with its value. 580d24de9dSSimon Glass 590d24de9dSSimon Glass Args: 600d24de9dSSimon Glass line: Source line containing tag (useful for debug/error messages) 610d24de9dSSimon Glass name: Tag name (part after 'Series-') 620d24de9dSSimon Glass value: Tag value (part after 'Series-xxx: ') 630d24de9dSSimon Glass """ 640d24de9dSSimon Glass # If we already have it, then add to our list 650d24de9dSSimon Glass if name in self: 660d24de9dSSimon Glass values = value.split(',') 670d24de9dSSimon Glass values = [str.strip() for str in values] 680d24de9dSSimon Glass if type(self[name]) != type([]): 690d24de9dSSimon Glass raise ValueError("In %s: line '%s': Cannot add another value " 700d24de9dSSimon Glass "'%s' to series '%s'" % 710d24de9dSSimon Glass (commit.hash, line, values, self[name])) 720d24de9dSSimon Glass self[name] += values 730d24de9dSSimon Glass 740d24de9dSSimon Glass # Otherwise just set the value 750d24de9dSSimon Glass elif name in valid_series: 760d24de9dSSimon Glass self[name] = value 770d24de9dSSimon Glass else: 780d24de9dSSimon Glass raise ValueError("In %s: line '%s': Unknown 'Series-%s': valid " 79*ef0e9de8SSimon Glass "options are %s" % (commit.hash, line, name, 800d24de9dSSimon Glass ', '.join(valid_series))) 810d24de9dSSimon Glass 820d24de9dSSimon Glass def AddCommit(self, commit): 830d24de9dSSimon Glass """Add a commit into our list of commits 840d24de9dSSimon Glass 850d24de9dSSimon Glass We create a list of tags in the commit subject also. 860d24de9dSSimon Glass 870d24de9dSSimon Glass Args: 880d24de9dSSimon Glass commit: Commit object to add 890d24de9dSSimon Glass """ 900d24de9dSSimon Glass commit.CheckTags() 910d24de9dSSimon Glass self.commits.append(commit) 920d24de9dSSimon Glass 930d24de9dSSimon Glass def ShowActions(self, args, cmd, process_tags): 940d24de9dSSimon Glass """Show what actions we will/would perform 950d24de9dSSimon Glass 960d24de9dSSimon Glass Args: 970d24de9dSSimon Glass args: List of patch files we created 980d24de9dSSimon Glass cmd: The git command we would have run 990d24de9dSSimon Glass process_tags: Process tags as if they were aliases 1000d24de9dSSimon Glass """ 1010d24de9dSSimon Glass col = terminal.Color() 1020d24de9dSSimon Glass print 'Dry run, so not doing much. But I would do this:' 1030d24de9dSSimon Glass print 1040d24de9dSSimon Glass print 'Send a total of %d patch%s with %scover letter.' % ( 1050d24de9dSSimon Glass len(args), '' if len(args) == 1 else 'es', 1060d24de9dSSimon Glass self.get('cover') and 'a ' or 'no ') 1070d24de9dSSimon Glass 1080d24de9dSSimon Glass # TODO: Colour the patches according to whether they passed checks 1090d24de9dSSimon Glass for upto in range(len(args)): 1100d24de9dSSimon Glass commit = self.commits[upto] 1110d24de9dSSimon Glass print col.Color(col.GREEN, ' %s' % args[upto]) 1120d24de9dSSimon Glass cc_list = [] 1130d24de9dSSimon Glass if process_tags: 1140d24de9dSSimon Glass cc_list += gitutil.BuildEmailList(commit.tags) 1150d24de9dSSimon Glass cc_list += gitutil.BuildEmailList(commit.cc_list) 1160d24de9dSSimon Glass 11743de0244SOtavio Salvador # Skip items in To list 11843de0244SOtavio Salvador if 'to' in self: 11943de0244SOtavio Salvador try: 12043de0244SOtavio Salvador map(cc_list.remove, gitutil.BuildEmailList(self.to)) 12143de0244SOtavio Salvador except ValueError: 12243de0244SOtavio Salvador pass 12343de0244SOtavio Salvador 1240d24de9dSSimon Glass for email in cc_list: 1250d24de9dSSimon Glass if email == None: 1260d24de9dSSimon Glass email = col.Color(col.YELLOW, "<alias '%s' not found>" 1270d24de9dSSimon Glass % tag) 1280d24de9dSSimon Glass if email: 1290d24de9dSSimon Glass print ' Cc: ',email 1300d24de9dSSimon Glass print 1310d24de9dSSimon Glass for item in gitutil.BuildEmailList(self.get('to', '<none>')): 1320d24de9dSSimon Glass print 'To:\t ', item 1330d24de9dSSimon Glass for item in gitutil.BuildEmailList(self.cc): 1340d24de9dSSimon Glass print 'Cc:\t ', item 1350d24de9dSSimon Glass print 'Version: ', self.get('version') 1360d24de9dSSimon Glass print 'Prefix:\t ', self.get('prefix') 1370d24de9dSSimon Glass if self.cover: 1380d24de9dSSimon Glass print 'Cover: %d lines' % len(self.cover) 1390d24de9dSSimon Glass if cmd: 1400d24de9dSSimon Glass print 'Git command: %s' % cmd 1410d24de9dSSimon Glass 1420d24de9dSSimon Glass def MakeChangeLog(self, commit): 1430d24de9dSSimon Glass """Create a list of changes for each version. 1440d24de9dSSimon Glass 1450d24de9dSSimon Glass Return: 1460d24de9dSSimon Glass The change log as a list of strings, one per line 1470d24de9dSSimon Glass 148244e6f97SOtavio Salvador Changes in v2: 149244e6f97SOtavio Salvador - Jog the dial back closer to the widget 150244e6f97SOtavio Salvador 1510d24de9dSSimon Glass Changes in v1: 1520d24de9dSSimon Glass - Fix the widget 1530d24de9dSSimon Glass - Jog the dial 1540d24de9dSSimon Glass 1550d24de9dSSimon Glass etc. 1560d24de9dSSimon Glass """ 1570d24de9dSSimon Glass final = [] 1580d24de9dSSimon Glass need_blank = False 159244e6f97SOtavio Salvador for change in sorted(self.changes, reverse=True): 1600d24de9dSSimon Glass out = [] 1610d24de9dSSimon Glass for this_commit, text in self.changes[change]: 1620d24de9dSSimon Glass if commit and this_commit != commit: 1630d24de9dSSimon Glass continue 1640d24de9dSSimon Glass out.append(text) 1650d24de9dSSimon Glass if out: 16673fe07a7SIlya Yanok out = ['Changes in v%d:' % change] + out 1670d24de9dSSimon Glass if need_blank: 1680d24de9dSSimon Glass out = [''] + out 1690d24de9dSSimon Glass final += out 1700d24de9dSSimon Glass need_blank = True 1710d24de9dSSimon Glass if self.changes: 1720d24de9dSSimon Glass final.append('') 1730d24de9dSSimon Glass return final 1740d24de9dSSimon Glass 1750d24de9dSSimon Glass def DoChecks(self): 1760d24de9dSSimon Glass """Check that each version has a change log 1770d24de9dSSimon Glass 1780d24de9dSSimon Glass Print an error if something is wrong. 1790d24de9dSSimon Glass """ 1800d24de9dSSimon Glass col = terminal.Color() 1810d24de9dSSimon Glass if self.get('version'): 1820d24de9dSSimon Glass changes_copy = dict(self.changes) 183d5f81d8aSOtavio Salvador for version in range(1, int(self.version) + 1): 1840d24de9dSSimon Glass if self.changes.get(version): 1850d24de9dSSimon Glass del changes_copy[version] 1860d24de9dSSimon Glass else: 187d5f81d8aSOtavio Salvador if version > 1: 1880d24de9dSSimon Glass str = 'Change log missing for v%d' % version 1890d24de9dSSimon Glass print col.Color(col.RED, str) 1900d24de9dSSimon Glass for version in changes_copy: 1910d24de9dSSimon Glass str = 'Change log for unknown version v%d' % version 1920d24de9dSSimon Glass print col.Color(col.RED, str) 1930d24de9dSSimon Glass elif self.changes: 1940d24de9dSSimon Glass str = 'Change log exists, but no version is set' 1950d24de9dSSimon Glass print col.Color(col.RED, str) 1960d24de9dSSimon Glass 1970d24de9dSSimon Glass def MakeCcFile(self, process_tags): 1980d24de9dSSimon Glass """Make a cc file for us to use for per-commit Cc automation 1990d24de9dSSimon Glass 2000d24de9dSSimon Glass Args: 2010d24de9dSSimon Glass process_tags: Process tags as if they were aliases 2020d24de9dSSimon Glass Return: 2030d24de9dSSimon Glass Filename of temp file created 2040d24de9dSSimon Glass """ 2050d24de9dSSimon Glass # Look for commit tags (of the form 'xxx:' at the start of the subject) 2060d24de9dSSimon Glass fname = '/tmp/patman.%d' % os.getpid() 2070d24de9dSSimon Glass fd = open(fname, 'w') 2080d24de9dSSimon Glass for commit in self.commits: 2090d24de9dSSimon Glass list = [] 2100d24de9dSSimon Glass if process_tags: 2110d24de9dSSimon Glass list += gitutil.BuildEmailList(commit.tags) 2120d24de9dSSimon Glass list += gitutil.BuildEmailList(commit.cc_list) 2130d24de9dSSimon Glass print >>fd, commit.patch, ', '.join(list) 2140d24de9dSSimon Glass 2150d24de9dSSimon Glass fd.close() 2160d24de9dSSimon Glass return fname 2170d24de9dSSimon Glass 2180d24de9dSSimon Glass def AddChange(self, version, commit, info): 2190d24de9dSSimon Glass """Add a new change line to a version. 2200d24de9dSSimon Glass 2210d24de9dSSimon Glass This will later appear in the change log. 2220d24de9dSSimon Glass 2230d24de9dSSimon Glass Args: 2240d24de9dSSimon Glass version: version number to add change list to 2250d24de9dSSimon Glass info: change line for this version 2260d24de9dSSimon Glass """ 2270d24de9dSSimon Glass if not self.changes.get(version): 2280d24de9dSSimon Glass self.changes[version] = [] 2290d24de9dSSimon Glass self.changes[version].append([commit, info]) 2300d24de9dSSimon Glass 2310d24de9dSSimon Glass def GetPatchPrefix(self): 2320d24de9dSSimon Glass """Get the patch version string 2330d24de9dSSimon Glass 2340d24de9dSSimon Glass Return: 2350d24de9dSSimon Glass Patch string, like 'RFC PATCH v5' or just 'PATCH' 2360d24de9dSSimon Glass """ 2370d24de9dSSimon Glass version = '' 2380d24de9dSSimon Glass if self.get('version'): 2390d24de9dSSimon Glass version = ' v%s' % self['version'] 2400d24de9dSSimon Glass 2410d24de9dSSimon Glass # Get patch name prefix 2420d24de9dSSimon Glass prefix = '' 2430d24de9dSSimon Glass if self.get('prefix'): 2440d24de9dSSimon Glass prefix = '%s ' % self['prefix'] 2450d24de9dSSimon Glass return '%sPATCH%s' % (prefix, version) 246