]> git.wh0rd.org - 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:]))