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