scripts: add a utility to fill blank fields of doc/README.scrapyard
authorMasahiro Yamada <yamada.m@jp.panasonic.com>
Wed, 14 Jan 2015 03:35:23 +0000 (12:35 +0900)
committerTom Rini <trini@ti.com>
Wed, 14 Jan 2015 15:58:49 +0000 (10:58 -0500)
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 <yamada.m@jp.panasonic.com>
Reviewed-by: Simon Glass <sjg@chromium.org>
scripts/fill_scrapyard.py [new file with mode: 0755]

diff --git a/scripts/fill_scrapyard.py b/scripts/fill_scrapyard.py
new file mode 100755 (executable)
index 0000000..9a94354
--- /dev/null
@@ -0,0 +1,166 @@
+#!/usr/bin/env python2
+#
+# Author: Masahiro Yamada <yamada.m@jp.panasonic.com>
+#
+# 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()