1#!/usr/bin/env python3
2# SPDX-License-Identifier: LGPL-2.1-or-later
3
4from __future__ import print_function
5import collections
6import glob
7import sys
8from pathlib import Path
9import pprint
10from xml_helper import xml_parse
11
12def man(page, number):
13    return '{}.{}'.format(page, number)
14
15def add_rules(rules, name):
16    xml = xml_parse(name)
17    # print('parsing {}'.format(name), file=sys.stderr)
18    if xml.getroot().tag != 'refentry':
19        return
20    conditional = xml.getroot().get('conditional') or ''
21    rulegroup = rules[conditional]
22    refmeta = xml.find('./refmeta')
23    title = refmeta.find('./refentrytitle').text
24    number = refmeta.find('./manvolnum').text
25    refnames = xml.findall('./refnamediv/refname')
26    target = man(refnames[0].text, number)
27    if title != refnames[0].text:
28        raise ValueError('refmeta and refnamediv disagree: ' + name)
29    for refname in refnames:
30        assert all(refname not in group
31                   for group in rules.values()), "duplicate page name"
32        alias = man(refname.text, number)
33        rulegroup[alias] = target
34        # print('{} => {} [{}]'.format(alias, target, conditional), file=sys.stderr)
35
36def create_rules(xml_files):
37    " {conditional => {alias-name => source-name}} "
38    rules = collections.defaultdict(dict)
39    for name in xml_files:
40        try:
41            add_rules(rules, name)
42        except Exception:
43            print("Failed to process", name, file=sys.stderr)
44            raise
45    return rules
46
47def mjoin(files):
48    return ' \\\n\t'.join(sorted(files) or '#')
49
50MESON_HEADER = '''\
51# SPDX-License-Identifier: LGPL-2.1-or-later
52
53# Do not edit. Generated by update-man-rules.py.
54# Update with:
55#     ninja -C build update-man-rules
56manpages = ['''
57
58MESON_FOOTER = '''\
59]
60# Really, do not edit.
61'''
62
63def make_mesonfile(rules, dist_files):
64    # reformat rules as
65    # grouped = [ [name, section, [alias...], condition], ...]
66    #
67    # but first create a dictionary like
68    # lists = { (name, condition) => [alias...]
69    grouped = collections.defaultdict(list)
70    for condition, items in rules.items():
71        for alias, name in items.items():
72            group = grouped[(name, condition)]
73            if name != alias:
74                group.append(alias)
75
76    lines = [ [p[0][:-2], p[0][-1], sorted(a[:-2] for a in aliases), p[1]]
77              for p, aliases in sorted(grouped.items()) ]
78    return '\n'.join((MESON_HEADER, pprint.pformat(lines)[1:-1], MESON_FOOTER))
79
80if __name__ == '__main__':
81    source_glob = sys.argv[1]
82    target = Path(sys.argv[2])
83
84    pages = glob.glob(source_glob)
85    pages = (p for p in pages
86             if Path(p).name not in {
87                     'systemd.directives.xml',
88                     'systemd.index.xml',
89                     'directives-template.xml'})
90
91    rules = create_rules(pages)
92    dist_files = (Path(p).name for p in pages)
93    text = make_mesonfile(rules, dist_files)
94
95    tmp = target.with_suffix('.tmp')
96    tmp.write_text(text)
97    tmp.rename(target)
98