]>
Commit | Line | Data |
---|---|---|
bd4ce675 MF |
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() | |
881429ea | 17 | mklog = _loader('mklog', '/usr/local/src/gnu/gcc/contrib/mklog.py') |
bd4ce675 MF |
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. | |
224d360b MF |
96 | # Specify the whitespace to strip explciitly as we want to leave the |
97 | # \f (^L) characters alone. | |
bd4ce675 | 98 | with open(log, encoding='utf-8') as fp: |
224d360b MF |
99 | old_log = '\n'.join(x.rstrip(' \t\r') |
100 | for x in fp.read().strip().split('\n')) | |
bd4ce675 MF |
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:])) |