1*4882a593Smuzhiyun#!/usr/bin/env python2 2*4882a593Smuzhiyun# 3*4882a593Smuzhiyun# Author: Masahiro Yamada <yamada.m@jp.panasonic.com> 4*4882a593Smuzhiyun# 5*4882a593Smuzhiyun# SPDX-License-Identifier: GPL-2.0+ 6*4882a593Smuzhiyun# 7*4882a593Smuzhiyun 8*4882a593Smuzhiyun""" 9*4882a593SmuzhiyunFill the "Commit" and "Removed" fields of doc/README.scrapyard 10*4882a593Smuzhiyun 11*4882a593SmuzhiyunThe file doc/README.scrapyard is used to keep track of removed boards. 12*4882a593Smuzhiyun 13*4882a593SmuzhiyunWhen we remove support for boards, we are supposed to add entries to 14*4882a593Smuzhiyundoc/README.scrapyard leaving "Commit" and "Removed" fields blank. 15*4882a593Smuzhiyun 16*4882a593SmuzhiyunThe "Commit" field is the commit hash in which the board was removed 17*4882a593Smuzhiyunand the "Removed" is the date at which the board was removed. Those 18*4882a593Smuzhiyuntwo are known only after the board removal patch was applied, thus they 19*4882a593Smuzhiyunneed to be filled in later. 20*4882a593Smuzhiyun 21*4882a593SmuzhiyunThis effectively means that the person who removes other boards is 22*4882a593Smuzhiyunsupposed to fill in the blank fields before adding new entries to 23*4882a593Smuzhiyundoc/README.scrapyard. 24*4882a593Smuzhiyun 25*4882a593SmuzhiyunThat is a really tedious task that should be automated. 26*4882a593SmuzhiyunThis script fills the blank fields of doc/README.scrapyard for you! 27*4882a593Smuzhiyun 28*4882a593SmuzhiyunUsage: 29*4882a593Smuzhiyun 30*4882a593SmuzhiyunThe "Commit" and "Removed" fields must be "-". The other fields should 31*4882a593Smuzhiyunhave already been filled in by a former commit. 32*4882a593Smuzhiyun 33*4882a593SmuzhiyunRun 34*4882a593Smuzhiyun scripts/fill_scrapyard.py 35*4882a593Smuzhiyun""" 36*4882a593Smuzhiyun 37*4882a593Smuzhiyunimport os 38*4882a593Smuzhiyunimport subprocess 39*4882a593Smuzhiyunimport sys 40*4882a593Smuzhiyunimport tempfile 41*4882a593Smuzhiyun 42*4882a593SmuzhiyunDOC='doc/README.scrapyard' 43*4882a593Smuzhiyun 44*4882a593Smuzhiyundef get_last_modify_commit(file, line_num): 45*4882a593Smuzhiyun """Get the commit that last modified the given line. 46*4882a593Smuzhiyun 47*4882a593Smuzhiyun This function runs "git blame" against the given line of the given 48*4882a593Smuzhiyun file and returns the commit hash that last modified it. 49*4882a593Smuzhiyun 50*4882a593Smuzhiyun Arguments: 51*4882a593Smuzhiyun file: the file to be git-blame'd. 52*4882a593Smuzhiyun line_num: the line number to be git-blame'd. This line number 53*4882a593Smuzhiyun starts from 1, not 0. 54*4882a593Smuzhiyun 55*4882a593Smuzhiyun Returns: 56*4882a593Smuzhiyun Commit hash that last modified the line. The number of digits is 57*4882a593Smuzhiyun long enough to form a unique commit. 58*4882a593Smuzhiyun """ 59*4882a593Smuzhiyun result = subprocess.check_output(['git', 'blame', '-L', 60*4882a593Smuzhiyun '%d,%d' % (line_num, line_num), file]) 61*4882a593Smuzhiyun commit = result.split()[0] 62*4882a593Smuzhiyun 63*4882a593Smuzhiyun if commit[0] == '^': 64*4882a593Smuzhiyun sys.exit('%s: line %d: ' % (file, line_num) + 65*4882a593Smuzhiyun 'this line was modified before the beginning of git history') 66*4882a593Smuzhiyun 67*4882a593Smuzhiyun if commit == '0' * len(commit): 68*4882a593Smuzhiyun sys.exit('%s: line %d: locally modified\n' % (file, line_num) + 69*4882a593Smuzhiyun 'Please run this script in a clean repository.') 70*4882a593Smuzhiyun 71*4882a593Smuzhiyun return commit 72*4882a593Smuzhiyun 73*4882a593Smuzhiyundef get_committer_date(commit): 74*4882a593Smuzhiyun """Get the committer date of the given commit. 75*4882a593Smuzhiyun 76*4882a593Smuzhiyun This function returns the date when the given commit was applied. 77*4882a593Smuzhiyun 78*4882a593Smuzhiyun Arguments: 79*4882a593Smuzhiyun commit: commit-ish object. 80*4882a593Smuzhiyun 81*4882a593Smuzhiyun Returns: 82*4882a593Smuzhiyun The committer date of the given commit in the form YY-MM-DD. 83*4882a593Smuzhiyun """ 84*4882a593Smuzhiyun committer_date = subprocess.check_output(['git', 'show', '-s', 85*4882a593Smuzhiyun '--format=%ci', commit]) 86*4882a593Smuzhiyun return committer_date.split()[0] 87*4882a593Smuzhiyun 88*4882a593Smuzhiyundef move_to_topdir(): 89*4882a593Smuzhiyun """Change directory to the top of the git repository. 90*4882a593Smuzhiyun 91*4882a593Smuzhiyun Or, exit with an error message if called out of a git repository. 92*4882a593Smuzhiyun """ 93*4882a593Smuzhiyun try: 94*4882a593Smuzhiyun toplevel = subprocess.check_output(['git', 'rev-parse', 95*4882a593Smuzhiyun '--show-toplevel']) 96*4882a593Smuzhiyun except subprocess.CalledProcessError: 97*4882a593Smuzhiyun sys.exit('Please run in a git repository.') 98*4882a593Smuzhiyun 99*4882a593Smuzhiyun # strip '\n' 100*4882a593Smuzhiyun toplevel = toplevel.rstrip() 101*4882a593Smuzhiyun 102*4882a593Smuzhiyun # Change the current working directory to the toplevel of the respository 103*4882a593Smuzhiyun # for our easier life. 104*4882a593Smuzhiyun os.chdir(toplevel) 105*4882a593Smuzhiyun 106*4882a593Smuzhiyunclass TmpFile: 107*4882a593Smuzhiyun 108*4882a593Smuzhiyun """Useful class to handle a temporary file. 109*4882a593Smuzhiyun 110*4882a593Smuzhiyun tempfile.mkstemp() is often used to create a unique temporary file, 111*4882a593Smuzhiyun but what is inconvenient is that the caller is responsible for 112*4882a593Smuzhiyun deleting the file when done with it. 113*4882a593Smuzhiyun 114*4882a593Smuzhiyun Even when the caller errors out on the way, the temporary file must 115*4882a593Smuzhiyun be deleted somehow. The idea here is that we delete the file in 116*4882a593Smuzhiyun the destructor of this class because the destructor is always 117*4882a593Smuzhiyun invoked when the instance of the class is freed. 118*4882a593Smuzhiyun """ 119*4882a593Smuzhiyun 120*4882a593Smuzhiyun def __init__(self): 121*4882a593Smuzhiyun """Constructor - create a temporary file""" 122*4882a593Smuzhiyun fd, self.filename = tempfile.mkstemp() 123*4882a593Smuzhiyun self.file = os.fdopen(fd, 'w') 124*4882a593Smuzhiyun 125*4882a593Smuzhiyun def __del__(self): 126*4882a593Smuzhiyun """Destructor - delete the temporary file""" 127*4882a593Smuzhiyun try: 128*4882a593Smuzhiyun os.remove(self.filename) 129*4882a593Smuzhiyun except: 130*4882a593Smuzhiyun pass 131*4882a593Smuzhiyun 132*4882a593Smuzhiyundef main(): 133*4882a593Smuzhiyun move_to_topdir() 134*4882a593Smuzhiyun 135*4882a593Smuzhiyun line_num = 1 136*4882a593Smuzhiyun 137*4882a593Smuzhiyun tmpfile = TmpFile() 138*4882a593Smuzhiyun for line in open(DOC): 139*4882a593Smuzhiyun tmp = line.split(None, 5) 140*4882a593Smuzhiyun modified = False 141*4882a593Smuzhiyun 142*4882a593Smuzhiyun if len(tmp) >= 5: 143*4882a593Smuzhiyun # fill "Commit" field 144*4882a593Smuzhiyun if tmp[3] == '-': 145*4882a593Smuzhiyun tmp[3] = get_last_modify_commit(DOC, line_num) 146*4882a593Smuzhiyun modified = True 147*4882a593Smuzhiyun # fill "Removed" field 148*4882a593Smuzhiyun if tmp[4] == '-': 149*4882a593Smuzhiyun tmp[4] = get_committer_date(tmp[3]) 150*4882a593Smuzhiyun if modified: 151*4882a593Smuzhiyun line = tmp[0].ljust(17) 152*4882a593Smuzhiyun line += tmp[1].ljust(12) 153*4882a593Smuzhiyun line += tmp[2].ljust(15) 154*4882a593Smuzhiyun line += tmp[3].ljust(12) 155*4882a593Smuzhiyun line += tmp[4].ljust(12) 156*4882a593Smuzhiyun if len(tmp) >= 6: 157*4882a593Smuzhiyun line += tmp[5] 158*4882a593Smuzhiyun line = line.rstrip() + '\n' 159*4882a593Smuzhiyun 160*4882a593Smuzhiyun tmpfile.file.write(line) 161*4882a593Smuzhiyun line_num += 1 162*4882a593Smuzhiyun 163*4882a593Smuzhiyun os.rename(tmpfile.filename, DOC) 164*4882a593Smuzhiyun 165*4882a593Smuzhiyunif __name__ == '__main__': 166*4882a593Smuzhiyun main() 167