]> git.wh0rd.org Git - home.git/blob - .bin/gnu-mklog
gnu-mklog: handle ^L whitespace chars
[home.git] / .bin / gnu-mklog
1 #!/usr/bin/env python3
2
3 """Helper for generating GNU ChangeLog entries."""
4
5 import argparse
6 import datetime
7 import importlib.machinery
8 import os
9 from pathlib import Path
10 import subprocess
11 import sys
12
13 import unidiff
14
15
16 _loader = lambda *args: importlib.machinery.SourceFileLoader(*args).load_module()
17 mklog = _loader('mklog', '/usr/local/src/gnu/gcc/git/contrib/mklog.py')
18 del _loader
19
20
21 NAME = 'Mike Frysinger  <vapier@gentoo.org>'
22 DATE = datetime.datetime.now().strftime('%Y-%m-%d')
23
24
25 def get_parser():
26     """Get CLI parser."""
27     parser = argparse.ArgumentParser(description=__doc__)
28     return parser
29
30
31 def main(argv):
32     """The main entry point for scripts."""
33     parser = get_parser()
34     opts = parser.parse_args(argv)
35
36     # Get the patchset from git.
37     full_diff = subprocess.run(
38         ['git', 'log', '-p', '--relative', '-1'],
39         check=True, capture_output=True, encoding='utf-8').stdout
40
41     # Filter out entries we don't want.
42     patchset = unidiff.PatchSet(full_diff)
43     i = 0
44     while i < len(patchset):
45         if Path(patchset[i].path).name.startswith('ChangeLog'):
46             patchset.pop(i)
47         else:
48             i += 1
49
50     # List of files that are always generated.
51     generated_files = {'aclocal.m4', 'config.in', 'configure'}
52
53     # Move the generated patches to the end of the patchset so the generated
54     # ChangeLog lists them at the end.
55     generated_patches = []
56     i = 0
57     while i < len(patchset):
58         if Path(patchset[i].path).name in generated_files:
59             generated_patches.append(patchset.pop(i))
60         else:
61             i += 1
62     patchset.extend(generated_patches)
63
64     # Find the ChangeLog for each path.
65     all_dirs = {Path(x.path).parent for x in patchset}
66     dirs_to_logs = {}
67     for index_dir in all_dirs:
68         d = index_dir
69         while True:
70             log = d / 'ChangeLog'
71             if log.is_file():
72                 break
73             d = d.parent
74         dirs_to_logs[index_dir] = log
75
76     # Group the patches based on the ChangeLogs they'll go into.
77     logs_to_patches = {}
78     for pfile in patchset:
79         log = dirs_to_logs[Path(pfile.path).parent]
80         ps = logs_to_patches.setdefault(log, unidiff.PatchSet(''))
81         ps.append(pfile)
82
83     # Now generate the logs for each subdir.
84     for log, ps in logs_to_patches.items():
85         new_log = mklog.generate_changelog(str(ps))
86         new_log = '\n'.join(new_log.splitlines()[2:]).rstrip()
87
88         # Hack: If we rebased the changes to a subdir, strip the path down.
89         # e.g. bfin/foo.c goes into bfin/ChangeLog, so drop bfin/ prefix.
90         for pfile in ps:
91             relpath = os.path.relpath(pfile.path, log.parent)
92             if relpath != pfile.path:
93                 new_log = new_log.replace(pfile.path, relpath)
94
95         # Read the old ChangeLog file and strip spurious whitespace.
96         # Specify the whitespace to strip explciitly as we want to leave the
97         # \f (^L) characters alone.
98         with open(log, encoding='utf-8') as fp:
99             old_log = '\n'.join(x.rstrip(' \t\r')
100                                 for x in fp.read().strip().split('\n'))
101
102         # Now update the ChangeLog file with the new entry at top.
103         with open(log, 'w', encoding='utf-8') as fp:
104             fp.write(f'{DATE}  {NAME}\n\n')
105             fp.write(new_log)
106             fp.write('\n\n')
107             fp.write(old_log)
108             fp.write('\n')
109
110
111 if __name__ == '__main__':
112     sys.exit(main(sys.argv[1:]))