Merge git://git.denx.de/u-boot-arc
[platform/kernel/u-boot.git] / scripts / fill_scrapyard.py
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 """
9 Fill the "Commit" and "Removed" fields of doc/README.scrapyard
10
11 The file doc/README.scrapyard is used to keep track of removed boards.
12
13 When we remove support for boards, we are supposed to add entries to
14 doc/README.scrapyard leaving "Commit" and "Removed" fields blank.
15
16 The "Commit" field is the commit hash in which the board was removed
17 and the "Removed" is the date at which the board was removed.  Those
18 two are known only after the board removal patch was applied, thus they
19 need to be filled in later.
20
21 This effectively means that the person who removes other boards is
22 supposed to fill in the blank fields before adding new entries to
23 doc/README.scrapyard.
24
25 That is a really tedious task that should be automated.
26 This script fills the blank fields of doc/README.scrapyard for you!
27
28 Usage:
29
30 The "Commit" and "Removed" fields must be "-".  The other fields should
31 have already been filled in by a former commit.
32
33 Run
34     scripts/fill_scrapyard.py
35 """
36
37 import os
38 import subprocess
39 import sys
40 import tempfile
41
42 DOC='doc/README.scrapyard'
43
44 def 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
73 def 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
88 def 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
106 class 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
132 def 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
165 if __name__ == '__main__':
166     main()