From 478d9372d19019891966ff0e8f709cf940b0ba56 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 14 Jan 2015 12:35:23 +0900 Subject: [PATCH] scripts: add a utility to fill blank fields of doc/README.scrapyard We are removing bunch of non-generic boards these days. Updating doc/README.scrapyard is a really tedious task, but it can be automated. I hope this tool will make our life easier. Signed-off-by: Masahiro Yamada Reviewed-by: Simon Glass --- scripts/fill_scrapyard.py | 166 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 166 insertions(+) create mode 100755 scripts/fill_scrapyard.py diff --git a/scripts/fill_scrapyard.py b/scripts/fill_scrapyard.py new file mode 100755 index 0000000000..9a94354941 --- /dev/null +++ b/scripts/fill_scrapyard.py @@ -0,0 +1,166 @@ +#!/usr/bin/env python2 +# +# Author: Masahiro Yamada +# +# SPDX-License-Identifier: GPL-2.0+ +# + +""" +Fill the "Commit" and "Removed" fields of doc/README.scrapyard + +The file doc/README.scrapyard is used to keep track of removed boards. + +When we remove support for boards, we are supposed to add entries to +doc/README.scrapyard leaving "Commit" and "Removed" fields blank. + +The "Commit" field is the commit hash in which the board was removed +and the "Removed" is the date at which the board was removed. Those +two are known only after the board removal patch was applied, thus they +need to be filled in later. + +This effectively means that the person who removes other boards is +supposed to fill in the blank fields before adding new entries to +doc/README.scrapyard. + +That is a really tedious task that should be automated. +This script fills the blank fields of doc/README.scrapyard for you! + +Usage: + +The "Commit" and "Removed" fields must be "-". The other fields should +have already been filled in by a former commit. + +Run + scripts/fill_scrapyard.py +""" + +import os +import subprocess +import sys +import tempfile + +DOC='doc/README.scrapyard' + +def get_last_modify_commit(file, line_num): + """Get the commit that last modified the given line. + + This function runs "git blame" against the given line of the given + file and returns the commit hash that last modified it. + + Arguments: + file: the file to be git-blame'd. + line_num: the line number to be git-blame'd. This line number + starts from 1, not 0. + + Returns: + Commit hash that last modified the line. The number of digits is + long enough to form a unique commit. + """ + result = subprocess.check_output(['git', 'blame', '-L', + '%d,%d' % (line_num, line_num), file]) + commit = result.split()[0] + + if commit[0] == '^': + sys.exit('%s: line %d: ' % (file, line_num) + + 'this line was modified before the beginning of git history') + + if commit == '0' * len(commit): + sys.exit('%s: line %d: locally modified\n' % (file, line_num) + + 'Please run this script in a clean repository.') + + return commit + +def get_committer_date(commit): + """Get the committer date of the given commit. + + This function returns the date when the given commit was applied. + + Arguments: + commit: commit-ish object. + + Returns: + The committer date of the given commit in the form YY-MM-DD. + """ + committer_date = subprocess.check_output(['git', 'show', '-s', + '--format=%ci', commit]) + return committer_date.split()[0] + +def move_to_topdir(): + """Change directory to the top of the git repository. + + Or, exit with an error message if called out of a git repository. + """ + try: + toplevel = subprocess.check_output(['git', 'rev-parse', + '--show-toplevel']) + except subprocess.CalledProcessError: + sys.exit('Please run in a git repository.') + + # strip '\n' + toplevel = toplevel.rstrip() + + # Change the current working directory to the toplevel of the respository + # for our easier life. + os.chdir(toplevel) + +class TmpFile: + + """Useful class to handle a temporary file. + + tempfile.mkstemp() is often used to create a unique temporary file, + but what is inconvenient is that the caller is responsible for + deleting the file when done with it. + + Even when the caller errors out on the way, the temporary file must + be deleted somehow. The idea here is that we delete the file in + the destructor of this class because the destructor is always + invoked when the instance of the class is freed. + """ + + def __init__(self): + """Constructor - create a temporary file""" + fd, self.filename = tempfile.mkstemp() + self.file = os.fdopen(fd, 'w') + + def __del__(self): + """Destructor - delete the temporary file""" + try: + os.remove(self.filename) + except: + pass + +def main(): + move_to_topdir() + + line_num = 1 + + tmpfile = TmpFile() + for line in open(DOC): + tmp = line.split(None, 5) + modified = False + + if len(tmp) >= 5: + # fill "Commit" field + if tmp[3] == '-': + tmp[3] = get_last_modify_commit(DOC, line_num) + modified = True + # fill "Removed" field + if tmp[4] == '-': + tmp[4] = get_committer_date(tmp[3]) + if modified: + line = tmp[0].ljust(17) + line += tmp[1].ljust(12) + line += tmp[2].ljust(15) + line += tmp[3].ljust(12) + line += tmp[4].ljust(12) + if len(tmp) >= 6: + line += tmp[5] + line = line.rstrip() + '\n' + + tmpfile.file.write(line) + line_num += 1 + + os.rename(tmpfile.filename, DOC) + +if __name__ == '__main__': + main() -- 2.25.1