10d24de9dSSimon Glass# Copyright (c) 2011 The Chromium OS Authors. 20d24de9dSSimon Glass# 3*1a459660SWolfgang Denk# SPDX-License-Identifier: GPL-2.0+ 40d24de9dSSimon Glass# 50d24de9dSSimon Glass 60d24de9dSSimon Glassimport os 70d24de9dSSimon Glassimport re 80d24de9dSSimon Glassimport shutil 90d24de9dSSimon Glassimport tempfile 100d24de9dSSimon Glass 110d24de9dSSimon Glassimport command 120d24de9dSSimon Glassimport commit 130d24de9dSSimon Glassimport gitutil 140d24de9dSSimon Glassfrom series import Series 150d24de9dSSimon Glass 160d24de9dSSimon Glass# Tags that we detect and remove 17619dd5deSSimon Glassre_remove = re.compile('^BUG=|^TEST=|^BRANCH=|^Change-Id:|^Review URL:' 183fefd5efSSimon Glass '|Reviewed-on:|Commit-\w*:') 190d24de9dSSimon Glass 200d24de9dSSimon Glass# Lines which are allowed after a TEST= line 210d24de9dSSimon Glassre_allowed_after_test = re.compile('^Signed-off-by:') 220d24de9dSSimon Glass 2305e5b735SIlya Yanok# Signoffs 2405e5b735SIlya Yanokre_signoff = re.compile('^Signed-off-by:') 2505e5b735SIlya Yanok 260d24de9dSSimon Glass# The start of the cover letter 270d24de9dSSimon Glassre_cover = re.compile('^Cover-letter:') 280d24de9dSSimon Glass 29fe2f8d9eSSimon Glass# A cover letter Cc 30fe2f8d9eSSimon Glassre_cover_cc = re.compile('^Cover-letter-cc: *(.*)') 31fe2f8d9eSSimon Glass 320d24de9dSSimon Glass# Patch series tag 33645b271aSSimon Glassre_series = re.compile('^Series-([a-z-]*): *(.*)') 340d24de9dSSimon Glass 350d24de9dSSimon Glass# Commit tags that we want to collect and keep 3628b3594eSDoug Andersonre_tag = re.compile('^(Tested-by|Acked-by|Reviewed-by|Cc): (.*)') 370d24de9dSSimon Glass 380d24de9dSSimon Glass# The start of a new commit in the git log 3968618281SDoug Andersonre_commit = re.compile('^commit ([0-9a-f]*)$') 400d24de9dSSimon Glass 410d24de9dSSimon Glass# We detect these since checkpatch doesn't always do it 420d24de9dSSimon Glassre_space_before_tab = re.compile('^[+].* \t') 430d24de9dSSimon Glass 440d24de9dSSimon Glass# States we can be in - can we use range() and still have comments? 450d24de9dSSimon GlassSTATE_MSG_HEADER = 0 # Still in the message header 460d24de9dSSimon GlassSTATE_PATCH_SUBJECT = 1 # In patch subject (first line of log for a commit) 470d24de9dSSimon GlassSTATE_PATCH_HEADER = 2 # In patch header (after the subject) 480d24de9dSSimon GlassSTATE_DIFFS = 3 # In the diff part (past --- line) 490d24de9dSSimon Glass 500d24de9dSSimon Glassclass PatchStream: 510d24de9dSSimon Glass """Class for detecting/injecting tags in a patch or series of patches 520d24de9dSSimon Glass 530d24de9dSSimon Glass We support processing the output of 'git log' to read out the tags we 540d24de9dSSimon Glass are interested in. We can also process a patch file in order to remove 550d24de9dSSimon Glass unwanted tags or inject additional ones. These correspond to the two 560d24de9dSSimon Glass phases of processing. 570d24de9dSSimon Glass """ 580d24de9dSSimon Glass def __init__(self, series, name=None, is_log=False): 590d24de9dSSimon Glass self.skip_blank = False # True to skip a single blank line 600d24de9dSSimon Glass self.found_test = False # Found a TEST= line 610d24de9dSSimon Glass self.lines_after_test = 0 # MNumber of lines found after TEST= 620d24de9dSSimon Glass self.warn = [] # List of warnings we have collected 630d24de9dSSimon Glass self.linenum = 1 # Output line number we are up to 640d24de9dSSimon Glass self.in_section = None # Name of start...END section we are in 650d24de9dSSimon Glass self.notes = [] # Series notes 660d24de9dSSimon Glass self.section = [] # The current section...END section 670d24de9dSSimon Glass self.series = series # Info about the patch series 680d24de9dSSimon Glass self.is_log = is_log # True if indent like git log 690d24de9dSSimon Glass self.in_change = 0 # Non-zero if we are in a change list 700d24de9dSSimon Glass self.blank_count = 0 # Number of blank lines stored up 710d24de9dSSimon Glass self.state = STATE_MSG_HEADER # What state are we in? 720d24de9dSSimon Glass self.tags = [] # Tags collected, like Tested-by... 730d24de9dSSimon Glass self.signoff = [] # Contents of signoff line 740d24de9dSSimon Glass self.commit = None # Current commit 750d24de9dSSimon Glass 760d24de9dSSimon Glass def AddToSeries(self, line, name, value): 770d24de9dSSimon Glass """Add a new Series-xxx tag. 780d24de9dSSimon Glass 790d24de9dSSimon Glass When a Series-xxx tag is detected, we come here to record it, if we 800d24de9dSSimon Glass are scanning a 'git log'. 810d24de9dSSimon Glass 820d24de9dSSimon Glass Args: 830d24de9dSSimon Glass line: Source line containing tag (useful for debug/error messages) 840d24de9dSSimon Glass name: Tag name (part after 'Series-') 850d24de9dSSimon Glass value: Tag value (part after 'Series-xxx: ') 860d24de9dSSimon Glass """ 870d24de9dSSimon Glass if name == 'notes': 880d24de9dSSimon Glass self.in_section = name 890d24de9dSSimon Glass self.skip_blank = False 900d24de9dSSimon Glass if self.is_log: 910d24de9dSSimon Glass self.series.AddTag(self.commit, line, name, value) 920d24de9dSSimon Glass 930d24de9dSSimon Glass def CloseCommit(self): 940d24de9dSSimon Glass """Save the current commit into our commit list, and reset our state""" 950d24de9dSSimon Glass if self.commit and self.is_log: 960d24de9dSSimon Glass self.series.AddCommit(self.commit) 970d24de9dSSimon Glass self.commit = None 980d24de9dSSimon Glass 990d24de9dSSimon Glass def FormatTags(self, tags): 1000d24de9dSSimon Glass out_list = [] 1010d24de9dSSimon Glass for tag in sorted(tags): 1020d24de9dSSimon Glass if tag.startswith('Cc:'): 1030d24de9dSSimon Glass tag_list = tag[4:].split(',') 1040d24de9dSSimon Glass out_list += gitutil.BuildEmailList(tag_list, 'Cc:') 1050d24de9dSSimon Glass else: 1060d24de9dSSimon Glass out_list.append(tag) 1070d24de9dSSimon Glass return out_list 1080d24de9dSSimon Glass 1090d24de9dSSimon Glass def ProcessLine(self, line): 1100d24de9dSSimon Glass """Process a single line of a patch file or commit log 1110d24de9dSSimon Glass 1120d24de9dSSimon Glass This process a line and returns a list of lines to output. The list 1130d24de9dSSimon Glass may be empty or may contain multiple output lines. 1140d24de9dSSimon Glass 1150d24de9dSSimon Glass This is where all the complicated logic is located. The class's 1160d24de9dSSimon Glass state is used to move between different states and detect things 1170d24de9dSSimon Glass properly. 1180d24de9dSSimon Glass 1190d24de9dSSimon Glass We can be in one of two modes: 1200d24de9dSSimon Glass self.is_log == True: This is 'git log' mode, where most output is 1210d24de9dSSimon Glass indented by 4 characters and we are scanning for tags 1220d24de9dSSimon Glass 1230d24de9dSSimon Glass self.is_log == False: This is 'patch' mode, where we already have 1240d24de9dSSimon Glass all the tags, and are processing patches to remove junk we 1250d24de9dSSimon Glass don't want, and add things we think are required. 1260d24de9dSSimon Glass 1270d24de9dSSimon Glass Args: 1280d24de9dSSimon Glass line: text line to process 1290d24de9dSSimon Glass 1300d24de9dSSimon Glass Returns: 1310d24de9dSSimon Glass list of output lines, or [] if nothing should be output 1320d24de9dSSimon Glass """ 1330d24de9dSSimon Glass # Initially we have no output. Prepare the input line string 1340d24de9dSSimon Glass out = [] 1350d24de9dSSimon Glass line = line.rstrip('\n') 1360d24de9dSSimon Glass if self.is_log: 1370d24de9dSSimon Glass if line[:4] == ' ': 1380d24de9dSSimon Glass line = line[4:] 1390d24de9dSSimon Glass 1400d24de9dSSimon Glass # Handle state transition and skipping blank lines 1410d24de9dSSimon Glass series_match = re_series.match(line) 1420d24de9dSSimon Glass commit_match = re_commit.match(line) if self.is_log else None 143fe2f8d9eSSimon Glass cover_cc_match = re_cover_cc.match(line) 1440d24de9dSSimon Glass tag_match = None 1450d24de9dSSimon Glass if self.state == STATE_PATCH_HEADER: 1460d24de9dSSimon Glass tag_match = re_tag.match(line) 1470d24de9dSSimon Glass is_blank = not line.strip() 1480d24de9dSSimon Glass if is_blank: 1490d24de9dSSimon Glass if (self.state == STATE_MSG_HEADER 1500d24de9dSSimon Glass or self.state == STATE_PATCH_SUBJECT): 1510d24de9dSSimon Glass self.state += 1 1520d24de9dSSimon Glass 1530d24de9dSSimon Glass # We don't have a subject in the text stream of patch files 1540d24de9dSSimon Glass # It has its own line with a Subject: tag 1550d24de9dSSimon Glass if not self.is_log and self.state == STATE_PATCH_SUBJECT: 1560d24de9dSSimon Glass self.state += 1 1570d24de9dSSimon Glass elif commit_match: 1580d24de9dSSimon Glass self.state = STATE_MSG_HEADER 1590d24de9dSSimon Glass 1600d24de9dSSimon Glass # If we are in a section, keep collecting lines until we see END 1610d24de9dSSimon Glass if self.in_section: 1620d24de9dSSimon Glass if line == 'END': 1630d24de9dSSimon Glass if self.in_section == 'cover': 1640d24de9dSSimon Glass self.series.cover = self.section 1650d24de9dSSimon Glass elif self.in_section == 'notes': 1660d24de9dSSimon Glass if self.is_log: 1670d24de9dSSimon Glass self.series.notes += self.section 1680d24de9dSSimon Glass else: 1690d24de9dSSimon Glass self.warn.append("Unknown section '%s'" % self.in_section) 1700d24de9dSSimon Glass self.in_section = None 1710d24de9dSSimon Glass self.skip_blank = True 1720d24de9dSSimon Glass self.section = [] 1730d24de9dSSimon Glass else: 1740d24de9dSSimon Glass self.section.append(line) 1750d24de9dSSimon Glass 1760d24de9dSSimon Glass # Detect the commit subject 1770d24de9dSSimon Glass elif not is_blank and self.state == STATE_PATCH_SUBJECT: 1780d24de9dSSimon Glass self.commit.subject = line 1790d24de9dSSimon Glass 1800d24de9dSSimon Glass # Detect the tags we want to remove, and skip blank lines 1810d24de9dSSimon Glass elif re_remove.match(line): 1820d24de9dSSimon Glass self.skip_blank = True 1830d24de9dSSimon Glass 1840d24de9dSSimon Glass # TEST= should be the last thing in the commit, so remove 1850d24de9dSSimon Glass # everything after it 1860d24de9dSSimon Glass if line.startswith('TEST='): 1870d24de9dSSimon Glass self.found_test = True 1880d24de9dSSimon Glass elif self.skip_blank and is_blank: 1890d24de9dSSimon Glass self.skip_blank = False 1900d24de9dSSimon Glass 1910d24de9dSSimon Glass # Detect the start of a cover letter section 1920d24de9dSSimon Glass elif re_cover.match(line): 1930d24de9dSSimon Glass self.in_section = 'cover' 1940d24de9dSSimon Glass self.skip_blank = False 1950d24de9dSSimon Glass 196fe2f8d9eSSimon Glass elif cover_cc_match: 197fe2f8d9eSSimon Glass value = cover_cc_match.group(1) 198fe2f8d9eSSimon Glass self.AddToSeries(line, 'cover-cc', value) 199fe2f8d9eSSimon Glass 2000d24de9dSSimon Glass # If we are in a change list, key collected lines until a blank one 2010d24de9dSSimon Glass elif self.in_change: 2020d24de9dSSimon Glass if is_blank: 2030d24de9dSSimon Glass # Blank line ends this change list 2040d24de9dSSimon Glass self.in_change = 0 20505e5b735SIlya Yanok elif line == '---' or re_signoff.match(line): 20605e5b735SIlya Yanok self.in_change = 0 20705e5b735SIlya Yanok out = self.ProcessLine(line) 2080d24de9dSSimon Glass else: 209a8840cb2SIlya Yanok if self.is_log: 2100d24de9dSSimon Glass self.series.AddChange(self.in_change, self.commit, line) 2110d24de9dSSimon Glass self.skip_blank = False 2120d24de9dSSimon Glass 2130d24de9dSSimon Glass # Detect Series-xxx tags 2140d24de9dSSimon Glass elif series_match: 2150d24de9dSSimon Glass name = series_match.group(1) 2160d24de9dSSimon Glass value = series_match.group(2) 2170d24de9dSSimon Glass if name == 'changes': 2180d24de9dSSimon Glass # value is the version number: e.g. 1, or 2 2190d24de9dSSimon Glass try: 2200d24de9dSSimon Glass value = int(value) 2210d24de9dSSimon Glass except ValueError as str: 2220d24de9dSSimon Glass raise ValueError("%s: Cannot decode version info '%s'" % 2230d24de9dSSimon Glass (self.commit.hash, line)) 2240d24de9dSSimon Glass self.in_change = int(value) 2250d24de9dSSimon Glass else: 2260d24de9dSSimon Glass self.AddToSeries(line, name, value) 2270d24de9dSSimon Glass self.skip_blank = True 2280d24de9dSSimon Glass 2290d24de9dSSimon Glass # Detect the start of a new commit 2300d24de9dSSimon Glass elif commit_match: 2310d24de9dSSimon Glass self.CloseCommit() 232e62f905eSSimon Glass # TODO: We should store the whole hash, and just display a subset 233e62f905eSSimon Glass self.commit = commit.Commit(commit_match.group(1)[:8]) 2340d24de9dSSimon Glass 2350d24de9dSSimon Glass # Detect tags in the commit message 2360d24de9dSSimon Glass elif tag_match: 2370d24de9dSSimon Glass # Remove Tested-by self, since few will take much notice 238c7379149SIlya Yanok if (tag_match.group(1) == 'Tested-by' and 2390d24de9dSSimon Glass tag_match.group(2).find(os.getenv('USER') + '@') != -1): 2400d24de9dSSimon Glass self.warn.append("Ignoring %s" % line) 2410d24de9dSSimon Glass elif tag_match.group(1) == 'Cc': 2420d24de9dSSimon Glass self.commit.AddCc(tag_match.group(2).split(',')) 2430d24de9dSSimon Glass else: 2440d24de9dSSimon Glass self.tags.append(line); 2450d24de9dSSimon Glass 2460d24de9dSSimon Glass # Well that means this is an ordinary line 2470d24de9dSSimon Glass else: 2480d24de9dSSimon Glass pos = 1 2490d24de9dSSimon Glass # Look for ugly ASCII characters 2500d24de9dSSimon Glass for ch in line: 2510d24de9dSSimon Glass # TODO: Would be nicer to report source filename and line 2520d24de9dSSimon Glass if ord(ch) > 0x80: 2530d24de9dSSimon Glass self.warn.append("Line %d/%d ('%s') has funny ascii char" % 2540d24de9dSSimon Glass (self.linenum, pos, line)) 2550d24de9dSSimon Glass pos += 1 2560d24de9dSSimon Glass 2570d24de9dSSimon Glass # Look for space before tab 2580d24de9dSSimon Glass m = re_space_before_tab.match(line) 2590d24de9dSSimon Glass if m: 2600d24de9dSSimon Glass self.warn.append('Line %d/%d has space before tab' % 2610d24de9dSSimon Glass (self.linenum, m.start())) 2620d24de9dSSimon Glass 2630d24de9dSSimon Glass # OK, we have a valid non-blank line 2640d24de9dSSimon Glass out = [line] 2650d24de9dSSimon Glass self.linenum += 1 2660d24de9dSSimon Glass self.skip_blank = False 2670d24de9dSSimon Glass if self.state == STATE_DIFFS: 2680d24de9dSSimon Glass pass 2690d24de9dSSimon Glass 2700d24de9dSSimon Glass # If this is the start of the diffs section, emit our tags and 2710d24de9dSSimon Glass # change log 2720d24de9dSSimon Glass elif line == '---': 2730d24de9dSSimon Glass self.state = STATE_DIFFS 2740d24de9dSSimon Glass 2750d24de9dSSimon Glass # Output the tags (signeoff first), then change list 2760d24de9dSSimon Glass out = [] 2770d24de9dSSimon Glass log = self.series.MakeChangeLog(self.commit) 2780d24de9dSSimon Glass out += self.FormatTags(self.tags) 2790d24de9dSSimon Glass out += [line] + log 2800d24de9dSSimon Glass elif self.found_test: 2810d24de9dSSimon Glass if not re_allowed_after_test.match(line): 2820d24de9dSSimon Glass self.lines_after_test += 1 2830d24de9dSSimon Glass 2840d24de9dSSimon Glass return out 2850d24de9dSSimon Glass 2860d24de9dSSimon Glass def Finalize(self): 2870d24de9dSSimon Glass """Close out processing of this patch stream""" 2880d24de9dSSimon Glass self.CloseCommit() 2890d24de9dSSimon Glass if self.lines_after_test: 2900d24de9dSSimon Glass self.warn.append('Found %d lines after TEST=' % 2910d24de9dSSimon Glass self.lines_after_test) 2920d24de9dSSimon Glass 2930d24de9dSSimon Glass def ProcessStream(self, infd, outfd): 2940d24de9dSSimon Glass """Copy a stream from infd to outfd, filtering out unwanting things. 2950d24de9dSSimon Glass 2960d24de9dSSimon Glass This is used to process patch files one at a time. 2970d24de9dSSimon Glass 2980d24de9dSSimon Glass Args: 2990d24de9dSSimon Glass infd: Input stream file object 3000d24de9dSSimon Glass outfd: Output stream file object 3010d24de9dSSimon Glass """ 3020d24de9dSSimon Glass # Extract the filename from each diff, for nice warnings 3030d24de9dSSimon Glass fname = None 3040d24de9dSSimon Glass last_fname = None 3050d24de9dSSimon Glass re_fname = re.compile('diff --git a/(.*) b/.*') 3060d24de9dSSimon Glass while True: 3070d24de9dSSimon Glass line = infd.readline() 3080d24de9dSSimon Glass if not line: 3090d24de9dSSimon Glass break 3100d24de9dSSimon Glass out = self.ProcessLine(line) 3110d24de9dSSimon Glass 3120d24de9dSSimon Glass # Try to detect blank lines at EOF 3130d24de9dSSimon Glass for line in out: 3140d24de9dSSimon Glass match = re_fname.match(line) 3150d24de9dSSimon Glass if match: 3160d24de9dSSimon Glass last_fname = fname 3170d24de9dSSimon Glass fname = match.group(1) 3180d24de9dSSimon Glass if line == '+': 3190d24de9dSSimon Glass self.blank_count += 1 3200d24de9dSSimon Glass else: 3210d24de9dSSimon Glass if self.blank_count and (line == '-- ' or match): 3220d24de9dSSimon Glass self.warn.append("Found possible blank line(s) at " 3230d24de9dSSimon Glass "end of file '%s'" % last_fname) 3240d24de9dSSimon Glass outfd.write('+\n' * self.blank_count) 3250d24de9dSSimon Glass outfd.write(line + '\n') 3260d24de9dSSimon Glass self.blank_count = 0 3270d24de9dSSimon Glass self.Finalize() 3280d24de9dSSimon Glass 3290d24de9dSSimon Glass 330e62f905eSSimon Glassdef GetMetaDataForList(commit_range, git_dir=None, count=None, 331e62f905eSSimon Glass series = Series()): 332e62f905eSSimon Glass """Reads out patch series metadata from the commits 333e62f905eSSimon Glass 334e62f905eSSimon Glass This does a 'git log' on the relevant commits and pulls out the tags we 335e62f905eSSimon Glass are interested in. 336e62f905eSSimon Glass 337e62f905eSSimon Glass Args: 338e62f905eSSimon Glass commit_range: Range of commits to count (e.g. 'HEAD..base') 339e62f905eSSimon Glass git_dir: Path to git repositiory (None to use default) 340e62f905eSSimon Glass count: Number of commits to list, or None for no limit 341e62f905eSSimon Glass series: Series object to add information into. By default a new series 342e62f905eSSimon Glass is started. 343e62f905eSSimon Glass Returns: 344e62f905eSSimon Glass A Series object containing information about the commits. 345e62f905eSSimon Glass """ 3462386060cSAndreas Bießmann params = ['git', 'log', '--no-color', '--reverse', '--no-decorate', 3472386060cSAndreas Bießmann commit_range] 348e62f905eSSimon Glass if count is not None: 349e62f905eSSimon Glass params[2:2] = ['-n%d' % count] 350e62f905eSSimon Glass if git_dir: 351e62f905eSSimon Glass params[1:1] = ['--git-dir', git_dir] 352e62f905eSSimon Glass pipe = [params] 353e62f905eSSimon Glass stdout = command.RunPipe(pipe, capture=True).stdout 354e62f905eSSimon Glass ps = PatchStream(series, is_log=True) 355e62f905eSSimon Glass for line in stdout.splitlines(): 356e62f905eSSimon Glass ps.ProcessLine(line) 357e62f905eSSimon Glass ps.Finalize() 358e62f905eSSimon Glass return series 359e62f905eSSimon Glass 3600d24de9dSSimon Glassdef GetMetaData(start, count): 3610d24de9dSSimon Glass """Reads out patch series metadata from the commits 3620d24de9dSSimon Glass 3630d24de9dSSimon Glass This does a 'git log' on the relevant commits and pulls out the tags we 3640d24de9dSSimon Glass are interested in. 3650d24de9dSSimon Glass 3660d24de9dSSimon Glass Args: 3670d24de9dSSimon Glass start: Commit to start from: 0=HEAD, 1=next one, etc. 3680d24de9dSSimon Glass count: Number of commits to list 3690d24de9dSSimon Glass """ 370e62f905eSSimon Glass return GetMetaDataForList('HEAD~%d' % start, None, count) 3710d24de9dSSimon Glass 3720d24de9dSSimon Glassdef FixPatch(backup_dir, fname, series, commit): 3730d24de9dSSimon Glass """Fix up a patch file, by adding/removing as required. 3740d24de9dSSimon Glass 3750d24de9dSSimon Glass We remove our tags from the patch file, insert changes lists, etc. 3760d24de9dSSimon Glass The patch file is processed in place, and overwritten. 3770d24de9dSSimon Glass 3780d24de9dSSimon Glass A backup file is put into backup_dir (if not None). 3790d24de9dSSimon Glass 3800d24de9dSSimon Glass Args: 3810d24de9dSSimon Glass fname: Filename to patch file to process 3820d24de9dSSimon Glass series: Series information about this patch set 3830d24de9dSSimon Glass commit: Commit object for this patch file 3840d24de9dSSimon Glass Return: 3850d24de9dSSimon Glass A list of errors, or [] if all ok. 3860d24de9dSSimon Glass """ 3870d24de9dSSimon Glass handle, tmpname = tempfile.mkstemp() 3880d24de9dSSimon Glass outfd = os.fdopen(handle, 'w') 3890d24de9dSSimon Glass infd = open(fname, 'r') 3900d24de9dSSimon Glass ps = PatchStream(series) 3910d24de9dSSimon Glass ps.commit = commit 3920d24de9dSSimon Glass ps.ProcessStream(infd, outfd) 3930d24de9dSSimon Glass infd.close() 3940d24de9dSSimon Glass outfd.close() 3950d24de9dSSimon Glass 3960d24de9dSSimon Glass # Create a backup file if required 3970d24de9dSSimon Glass if backup_dir: 3980d24de9dSSimon Glass shutil.copy(fname, os.path.join(backup_dir, os.path.basename(fname))) 3990d24de9dSSimon Glass shutil.move(tmpname, fname) 4000d24de9dSSimon Glass return ps.warn 4010d24de9dSSimon Glass 4020d24de9dSSimon Glassdef FixPatches(series, fnames): 4030d24de9dSSimon Glass """Fix up a list of patches identified by filenames 4040d24de9dSSimon Glass 4050d24de9dSSimon Glass The patch files are processed in place, and overwritten. 4060d24de9dSSimon Glass 4070d24de9dSSimon Glass Args: 4080d24de9dSSimon Glass series: The series object 4090d24de9dSSimon Glass fnames: List of patch files to process 4100d24de9dSSimon Glass """ 4110d24de9dSSimon Glass # Current workflow creates patches, so we shouldn't need a backup 4120d24de9dSSimon Glass backup_dir = None #tempfile.mkdtemp('clean-patch') 4130d24de9dSSimon Glass count = 0 4140d24de9dSSimon Glass for fname in fnames: 4150d24de9dSSimon Glass commit = series.commits[count] 4160d24de9dSSimon Glass commit.patch = fname 4170d24de9dSSimon Glass result = FixPatch(backup_dir, fname, series, commit) 4180d24de9dSSimon Glass if result: 4190d24de9dSSimon Glass print '%d warnings for %s:' % (len(result), fname) 4200d24de9dSSimon Glass for warn in result: 4210d24de9dSSimon Glass print '\t', warn 4220d24de9dSSimon Glass print 4230d24de9dSSimon Glass count += 1 4240d24de9dSSimon Glass print 'Cleaned %d patches' % count 4250d24de9dSSimon Glass return series 4260d24de9dSSimon Glass 4270d24de9dSSimon Glassdef InsertCoverLetter(fname, series, count): 4280d24de9dSSimon Glass """Inserts a cover letter with the required info into patch 0 4290d24de9dSSimon Glass 4300d24de9dSSimon Glass Args: 4310d24de9dSSimon Glass fname: Input / output filename of the cover letter file 4320d24de9dSSimon Glass series: Series object 4330d24de9dSSimon Glass count: Number of patches in the series 4340d24de9dSSimon Glass """ 4350d24de9dSSimon Glass fd = open(fname, 'r') 4360d24de9dSSimon Glass lines = fd.readlines() 4370d24de9dSSimon Glass fd.close() 4380d24de9dSSimon Glass 4390d24de9dSSimon Glass fd = open(fname, 'w') 4400d24de9dSSimon Glass text = series.cover 4410d24de9dSSimon Glass prefix = series.GetPatchPrefix() 4420d24de9dSSimon Glass for line in lines: 4430d24de9dSSimon Glass if line.startswith('Subject:'): 4440d24de9dSSimon Glass # TODO: if more than 10 patches this should save 00/xx, not 0/xx 4450d24de9dSSimon Glass line = 'Subject: [%s 0/%d] %s\n' % (prefix, count, text[0]) 4460d24de9dSSimon Glass 4470d24de9dSSimon Glass # Insert our cover letter 4480d24de9dSSimon Glass elif line.startswith('*** BLURB HERE ***'): 4490d24de9dSSimon Glass # First the blurb test 4500d24de9dSSimon Glass line = '\n'.join(text[1:]) + '\n' 4510d24de9dSSimon Glass if series.get('notes'): 4520d24de9dSSimon Glass line += '\n'.join(series.notes) + '\n' 4530d24de9dSSimon Glass 4540d24de9dSSimon Glass # Now the change list 4550d24de9dSSimon Glass out = series.MakeChangeLog(None) 4560d24de9dSSimon Glass line += '\n' + '\n'.join(out) 4570d24de9dSSimon Glass fd.write(line) 4580d24de9dSSimon Glass fd.close() 459