[systemd-devel] [PATCH 1/2] build-sys: move python helpers to tools directory

Karel Zak kzak at redhat.com
Mon Feb 10 01:37:10 PST 2014


Note that make-man-rules.py is missing in EXTRA_DIST=, this patch
fixes this mistake too.
---
 Makefile.am                   |  13 +-
 make-directive-index.py       | 320 ------------------------------------------
 make-man-index.py             | 136 ------------------
 make-man-rules.py             | 113 ---------------
 tools/make-directive-index.py | 320 ++++++++++++++++++++++++++++++++++++++++++
 tools/make-man-index.py       | 136 ++++++++++++++++++
 tools/make-man-rules.py       | 113 +++++++++++++++
 tools/xml_helper.py           |  41 ++++++
 xml_helper.py                 |  41 ------
 9 files changed, 617 insertions(+), 616 deletions(-)
 delete mode 100755 make-directive-index.py
 delete mode 100755 make-man-index.py
 delete mode 100644 make-man-rules.py
 create mode 100755 tools/make-directive-index.py
 create mode 100755 tools/make-man-index.py
 create mode 100644 tools/make-man-rules.py
 create mode 100644 tools/xml_helper.py
 delete mode 100644 xml_helper.py

diff --git a/Makefile.am b/Makefile.am
index 4f5e036..e3c1145 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -610,16 +610,16 @@ XML_GLOB = $(wildcard $(top_srcdir)/man/*.xml $(top_builddir)/man/*.xml)
 NON_INDEX_XML_FILES = $(filter-out man/systemd.index.xml,$(XML_FILES))
 SOURCE_XML_FILES = $(filter-out man/systemd.directives.xml,$(NON_INDEX_XML_FILES))
 
-update-man-list: make-man-rules.py $(XML_GLOB)
+update-man-list: $(top_srcdir)/tools/make-man-rules.py $(XML_GLOB)
 	$(AM_V_GEN)$(PYTHON) $^ > $(top_srcdir)/Makefile-man.tmp
 	$(AM_V_at)mv $(top_srcdir)/Makefile-man.tmp $(top_srcdir)/Makefile-man.am
 	@echo "Makefile-man.am has been regenerated"
 
-man/systemd.index.xml: make-man-index.py $(NON_INDEX_XML_FILES)
+man/systemd.index.xml: $(top_srcdir)/tools/make-man-index.py $(NON_INDEX_XML_FILES)
 	$(AM_V_at)$(MKDIR_P) $(dir $@)
 	$(AM_V_GEN)$(PYTHON) $< $@ $(filter-out $<,$^)
 
-man/systemd.directives.xml: make-directive-index.py $(SOURCE_XML_FILES)
+man/systemd.directives.xml: $(top_srcdir)/tools/make-directive-index.py $(SOURCE_XML_FILES)
 	$(AM_V_at)$(MKDIR_P) $(dir $@)
 	$(AM_V_GEN)$(PYTHON) $< $@ $(filter-out $<,$^)
 
@@ -641,9 +641,10 @@ EXTRA_DIST += \
 	$(HTML_FILES) \
 	$(HTML_ALIAS) \
 	$(man_MANS) \
-	make-man-index.py \
-	make-directive-index.py \
-	xml_helper.py
+	tools/make-man-index.py \
+	tools/make-directive-index.py \
+	tools/make-man-rules.py \
+	tools/xml_helper.py
 
 # ------------------------------------------------------------------------------
 noinst_LTLIBRARIES += \
diff --git a/make-directive-index.py b/make-directive-index.py
deleted file mode 100755
index 2ff304f..0000000
--- a/make-directive-index.py
+++ /dev/null
@@ -1,320 +0,0 @@
-#  -*- Mode: python; coding: utf-8; indent-tabs-mode: nil -*- */
-#
-#  This file is part of systemd.
-#
-#  Copyright 2012-2013 Zbigniew Jędrzejewski-Szmek
-#
-#  systemd is free software; you can redistribute it and/or modify it
-#  under the terms of the GNU Lesser General Public License as published by
-#  the Free Software Foundation; either version 2.1 of the License, or
-#  (at your option) any later version.
-#
-#  systemd is distributed in the hope that it will be useful, but
-#  WITHOUT ANY WARRANTY; without even the implied warranty of
-#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-#  Lesser General Public License for more details.
-#
-#  You should have received a copy of the GNU Lesser General Public License
-#  along with systemd; If not, see <http://www.gnu.org/licenses/>.
-
-import sys
-import collections
-import re
-from xml_helper import *
-from copy import deepcopy
-
-TEMPLATE = '''\
-<refentry id="systemd.directives" conditional="HAVE_PYTHON">
-
-        <refentryinfo>
-                <title>systemd.directives</title>
-                <productname>systemd</productname>
-
-                <authorgroup>
-                        <author>
-                                <contrib>Developer</contrib>
-                                <firstname>Zbigniew</firstname>
-                                <surname>Jędrzejewski-Szmek</surname>
-                                <email>zbyszek at in.waw.pl</email>
-                        </author>
-                </authorgroup>
-        </refentryinfo>
-
-        <refmeta>
-                <refentrytitle>systemd.directives</refentrytitle>
-                <manvolnum>7</manvolnum>
-        </refmeta>
-
-        <refnamediv>
-                <refname>systemd.directives</refname>
-                <refpurpose>Index of configuration directives</refpurpose>
-        </refnamediv>
-
-        <refsect1>
-                <title>Unit directives</title>
-
-                <para>Directives for configuring units, used in unit
-                files.</para>
-
-                <variablelist id='unit-directives' />
-        </refsect1>
-
-        <refsect1>
-                <title>Options on the kernel command line</title>
-
-                <para>Kernel boot options for configuring the behaviour of the
-                systemd process.</para>
-
-                <variablelist id='kernel-commandline-options' />
-        </refsect1>
-
-        <refsect1>
-                <title>Environment variables</title>
-
-                <para>Environment variables understood by the systemd
-                manager and other programs.</para>
-
-                <variablelist id='environment-variables' />
-        </refsect1>
-
-        <refsect1>
-                <title>UDEV directives</title>
-
-                <para>Directives for configuring systemd units through the
-                udev database.</para>
-
-                <variablelist id='udev-directives' />
-        </refsect1>
-
-        <refsect1>
-                <title>Network directives</title>
-
-                <para>Directives for configuring network links through the
-                net-setup-link udev builtin and networks through
-                systemd-networkd.</para>
-
-                <variablelist id='network-directives' />
-        </refsect1>
-
-        <refsect1>
-                <title>Journal fields</title>
-
-                <para>Fields in the journal events with a well known meaning.</para>
-
-                <variablelist id='journal-directives' />
-        </refsect1>
-
-        <refsect1>
-                <title>PAM configuration directives</title>
-
-                <para>Directives for configuring PAM behaviour.</para>
-
-                <variablelist id='pam-directives' />
-        </refsect1>
-
-        <refsect1>
-                <title>crypttab options</title>
-
-                <para>Options which influence mounted filesystems and
-                encrypted volumes.</para>
-
-                <variablelist id='crypttab-options' />
-        </refsect1>
-
-        <refsect1>
-                <title>System manager directives</title>
-
-                <para>Directives for configuring the behaviour of the
-                systemd process.</para>
-
-                <variablelist id='systemd-directives' />
-        </refsect1>
-
-        <refsect1>
-                <title>bootchart.conf directives</title>
-
-                <para>Directives for configuring the behaviour of the
-                systemd-bootchart process.</para>
-
-                <variablelist id='bootchart-directives' />
-        </refsect1>
-
-        <refsect1>
-                <title>command-line options</title>
-
-                <para>Command-line options accepted by programs in the
-                systemd suite.</para>
-
-                <variablelist id='options' />
-        </refsect1>
-
-        <refsect1>
-                <title>Constants</title>
-
-                <para>Various constant used and/or defined by systemd.</para>
-
-                <variablelist id='constants' />
-        </refsect1>
-
-        <refsect1>
-                <title>Miscellaneous options and directives</title>
-
-                <para>Other configuration elements which don't fit in
-                any of the above groups.</para>
-
-                <variablelist id='miscellaneous' />
-        </refsect1>
-
-        <refsect1>
-                <title>Files and directories</title>
-
-                <para>Paths and file names referred to in the
-                documentation.</para>
-
-                <variablelist id='filenames' />
-        </refsect1>
-
-        <refsect1>
-                <title>Colophon</title>
-                <para id='colophon' />
-        </refsect1>
-</refentry>
-'''
-
-COLOPHON = '''\
-This index contains {count} entries in {sections} sections,
-referring to {pages} individual manual pages.
-'''
-
-def _extract_directives(directive_groups, formatting, page):
-    t = xml_parse(page)
-    section = t.find('./refmeta/manvolnum').text
-    pagename = t.find('./refmeta/refentrytitle').text
-
-    storopt = directive_groups['options']
-    for variablelist in t.iterfind('.//variablelist'):
-        klass = variablelist.attrib.get('class')
-        storvar = directive_groups[klass or 'miscellaneous']
-        # <option>s go in OPTIONS, unless class is specified
-        for xpath, stor in (('./varlistentry/term/varname', storvar),
-                            ('./varlistentry/term/option',
-                             storvar if klass else storopt)):
-            for name in variablelist.iterfind(xpath):
-                text = re.sub(r'([= ]).*', r'\1', name.text).rstrip()
-                stor[text].append((pagename, section))
-                if text not in formatting:
-                    # use element as formatted display
-                    if name.text[-1] in '= ':
-                        name.clear()
-                    else:
-                        name.tail = ''
-                    name.text = text
-                    formatting[text] = name
-
-    storfile = directive_groups['filenames']
-    for xpath, absolute_only in (('.//refsynopsisdiv//filename', False),
-                                 ('.//refsynopsisdiv//command', False),
-                                 ('.//filename', True)):
-        for name in t.iterfind(xpath):
-            if absolute_only and not (name.text and name.text.startswith('/')):
-                continue
-            if name.attrib.get('noindex'):
-                continue
-            name.tail = ''
-            if name.text:
-                if name.text.endswith('*'):
-                    name.text = name.text[:-1]
-                if not name.text.startswith('.'):
-                    text = name.text.partition(' ')[0]
-                    if text != name.text:
-                        name.clear()
-                        name.text = text
-                    if text.endswith('/'):
-                        text = text[:-1]
-                    storfile[text].append((pagename, section))
-                    if text not in formatting:
-                        # use element as formatted display
-                        formatting[text] = name
-            else:
-                text = ' '.join(name.itertext())
-                storfile[text].append((pagename, section))
-                formatting[text] = name
-
-    storfile = directive_groups['constants']
-    for name in t.iterfind('.//constant'):
-        if name.attrib.get('noindex'):
-            continue
-        name.tail = ''
-        if name.text.startswith('('): # a cast, strip it
-            name.text = name.text.partition(' ')[2]
-        storfile[name.text].append((pagename, section))
-        formatting[name.text] = name
-
-def _make_section(template, name, directives, formatting):
-    varlist = template.find(".//*[@id='{}']".format(name))
-    for varname, manpages in sorted(directives.items()):
-        entry = tree.SubElement(varlist, 'varlistentry')
-        term = tree.SubElement(entry, 'term')
-        display = deepcopy(formatting[varname])
-        term.append(display)
-
-        para = tree.SubElement(tree.SubElement(entry, 'listitem'), 'para')
-
-        b = None
-        for manpage, manvolume in sorted(set(manpages)):
-            if b is not None:
-                b.tail = ', '
-            b = tree.SubElement(para, 'citerefentry')
-            c = tree.SubElement(b, 'refentrytitle')
-            c.text = manpage
-            d = tree.SubElement(b, 'manvolnum')
-            d.text = manvolume
-        entry.tail = '\n\n'
-
-def _make_colophon(template, groups):
-    count = 0
-    pages = set()
-    for group in groups:
-        count += len(group)
-        for pagelist in group.values():
-            pages |= set(pagelist)
-
-    para = template.find(".//para[@id='colophon']")
-    para.text = COLOPHON.format(count=count,
-                                sections=len(groups),
-                                pages=len(pages))
-
-def _make_page(template, directive_groups, formatting):
-    """Create an XML tree from directive_groups.
-
-    directive_groups = {
-       'class': {'variable': [('manpage', 'manvolume'), ...],
-                 'variable2': ...},
-       ...
-    }
-    """
-    for name, directives in directive_groups.items():
-        _make_section(template, name, directives, formatting)
-
-    _make_colophon(template, directive_groups.values())
-
-    return template
-
-def make_page(*xml_files):
-    "Extract directives from xml_files and return XML index tree."
-    template = tree.fromstring(TEMPLATE)
-    names = [vl.get('id') for vl in template.iterfind('.//variablelist')]
-    directive_groups = {name:collections.defaultdict(list)
-                        for name in names}
-    formatting = {}
-    for page in xml_files:
-        try:
-            _extract_directives(directive_groups, formatting, page)
-        except Exception:
-            raise ValueError("failed to process " + page)
-
-    return _make_page(template, directive_groups, formatting)
-
-if __name__ == '__main__':
-    with open(sys.argv[1], 'wb') as f:
-        f.write(xml_print(make_page(*sys.argv[2:])))
diff --git a/make-man-index.py b/make-man-index.py
deleted file mode 100755
index 74a47b8..0000000
--- a/make-man-index.py
+++ /dev/null
@@ -1,136 +0,0 @@
-#  -*- Mode: python; coding: utf-8; indent-tabs-mode: nil -*- */
-#
-#  This file is part of systemd.
-#
-#  Copyright 2012 Lennart Poettering
-#  Copyright 2013 Zbigniew Jędrzejewski-Szmek
-#
-#  systemd is free software; you can redistribute it and/or modify it
-#  under the terms of the GNU Lesser General Public License as published by
-#  the Free Software Foundation; either version 2.1 of the License, or
-#  (at your option) any later version.
-#
-#  systemd is distributed in the hope that it will be useful, but
-#  WITHOUT ANY WARRANTY; without even the implied warranty of
-#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-#  Lesser General Public License for more details.
-#
-#  You should have received a copy of the GNU Lesser General Public License
-#  along with systemd; If not, see <http://www.gnu.org/licenses/>.
-
-import collections
-import sys
-import re
-from xml_helper import *
-
-MDASH = ' — ' if sys.version_info.major >= 3 else ' -- '
-
-TEMPLATE = '''\
-<refentry id="systemd.index" conditional="HAVE_PYTHON">
-
-  <refentryinfo>
-    <title>systemd.index</title>
-    <productname>systemd</productname>
-
-    <authorgroup>
-      <author>
-        <contrib>Developer</contrib>
-        <firstname>Lennart</firstname>
-        <surname>Poettering</surname>
-        <email>lennart at poettering.net</email>
-      </author>
-    </authorgroup>
-  </refentryinfo>
-
-  <refmeta>
-    <refentrytitle>systemd.index</refentrytitle>
-    <manvolnum>7</manvolnum>
-  </refmeta>
-
-  <refnamediv>
-    <refname>systemd.index</refname>
-    <refpurpose>List all manpages from the systemd project</refpurpose>
-  </refnamediv>
-</refentry>
-'''
-
-SUMMARY = '''\
-  <refsect1>
-    <title>See Also</title>
-    <para>
-      <citerefentry><refentrytitle>systemd.directives</refentrytitle><manvolnum>7</manvolnum></citerefentry>
-    </para>
-
-    <para id='counts' />
-  </refsect1>
-'''
-
-COUNTS = '\
-This index contains {count} entries, referring to {pages} individual manual pages.'
-
-
-def check_id(page, t):
-    id = t.getroot().get('id')
-    if not re.search('/' + id + '[.]', page):
-        raise ValueError("id='{}' is not the same as page name '{}'".format(id, page))
-
-def make_index(pages):
-    index = collections.defaultdict(list)
-    for p in pages:
-        t = xml_parse(p)
-        check_id(p, t)
-        section = t.find('./refmeta/manvolnum').text
-        refname = t.find('./refnamediv/refname').text
-        purpose = ' '.join(t.find('./refnamediv/refpurpose').text.split())
-        for f in t.findall('./refnamediv/refname'):
-            infos = (f.text, section, purpose, refname)
-            index[f.text[0].upper()].append(infos)
-    return index
-
-def add_letter(template, letter, pages):
-    refsect1 = tree.SubElement(template, 'refsect1')
-    title = tree.SubElement(refsect1, 'title')
-    title.text = letter
-    para = tree.SubElement(refsect1, 'para')
-    for info in sorted(pages, key=lambda info: str.lower(info[0])):
-        refname, section, purpose, realname = info
-
-        b = tree.SubElement(para, 'citerefentry')
-        c = tree.SubElement(b, 'refentrytitle')
-        c.text = refname
-        d = tree.SubElement(b, 'manvolnum')
-        d.text = section
-
-        b.tail = MDASH + purpose # + ' (' + p + ')'
-
-        tree.SubElement(para, 'sbr')
-
-def add_summary(template, indexpages):
-    count = 0
-    pages = set()
-    for group in indexpages:
-        count += len(group)
-        for info in group:
-            refname, section, purpose, realname = info
-            pages.add((realname, section))
-
-    refsect1 = tree.fromstring(SUMMARY)
-    template.append(refsect1)
-
-    para = template.find(".//para[@id='counts']")
-    para.text = COUNTS.format(count=count, pages=len(pages))
-
-def make_page(*xml_files):
-    template = tree.fromstring(TEMPLATE)
-    index = make_index(xml_files)
-
-    for letter in sorted(index):
-        add_letter(template, letter, index[letter])
-
-    add_summary(template, index.values())
-
-    return template
-
-if __name__ == '__main__':
-    with open(sys.argv[1], 'wb') as f:
-        f.write(xml_print(make_page(*sys.argv[2:])))
diff --git a/make-man-rules.py b/make-man-rules.py
deleted file mode 100644
index 0d1ca24..0000000
--- a/make-man-rules.py
+++ /dev/null
@@ -1,113 +0,0 @@
-#  -*- Mode: python; coding: utf-8; indent-tabs-mode: nil -*- */
-#
-#  This file is part of systemd.
-#
-#  Copyright 2013 Zbigniew Jędrzejewski-Szmek
-#
-#  systemd is free software; you can redistribute it and/or modify it
-#  under the terms of the GNU Lesser General Public License as published by
-#  the Free Software Foundation; either version 2.1 of the License, or
-#  (at your option) any later version.
-#
-#  systemd is distributed in the hope that it will be useful, but
-#  WITHOUT ANY WARRANTY; without even the implied warranty of
-#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-#  Lesser General Public License for more details.
-#
-#  You should have received a copy of the GNU Lesser General Public License
-#  along with systemd; If not, see <http://www.gnu.org/licenses/>.
-
-from __future__ import print_function
-import collections
-import sys
-import os.path
-from xml_helper import *
-
-SECTION = '''\
-MANPAGES += \\
-	{manpages}
-MANPAGES_ALIAS += \\
-	{aliases}
-{rules}
-{htmlrules}
-'''
-
-CONDITIONAL = '''\
-if {conditional}
-''' \
-+ SECTION + \
-'''\
-endif
-'''
-
-HEADER = '''\
-# Do not edit. Generated by make-man-rules.py.
-# Regenerate with 'make all update-man-list'.
-
-'''
-
-HTML_ALIAS_RULE = '''\
-{}.html: {}.html
-	$(html-alias)
-'''
-
-FOOTER = '''\
-
-EXTRA_DIST += \\
-       {files}
-'''
-
-def man(page, number):
-    return 'man/{}.{}'.format(page, number)
-
-def xml(file):
-    return 'man/{}'.format(os.path.basename(file))
-
-def add_rules(rules, name):
-    xml = xml_parse(name)
-    # print('parsing {}'.format(name), file=sys.stderr)
-    conditional = xml.getroot().get('conditional') or ''
-    rulegroup = rules[conditional]
-    refmeta = xml.find('./refmeta')
-    title = refmeta.find('./refentrytitle').text
-    number = refmeta.find('./manvolnum').text
-    refnames = xml.findall('./refnamediv/refname')
-    target = man(refnames[0].text, number)
-    if title != refnames[0].text:
-        raise ValueError('refmeta and refnamediv disagree: ' + name)
-    for refname in refnames:
-        assert all(refname not in group
-                   for group in rules.values()), "duplicate page name"
-        alias = man(refname.text, number)
-        rulegroup[alias] = target
-        # print('{} => {} [{}]'.format(alias, target, conditional), file=sys.stderr)
-
-def create_rules(xml_files):
-    " {conditional => {alias-name => source-name}} "
-    rules = collections.defaultdict(dict)
-    for name in xml_files:
-        add_rules(rules, name)
-    return rules
-
-def mjoin(files):
-    return ' \\\n\t'.join(sorted(files) or '#')
-
-def make_makefile(rules, files):
-    return HEADER + '\n'.join(
-        (CONDITIONAL if conditional else SECTION).format(
-            manpages=mjoin(set(rulegroup.values())),
-            aliases=mjoin(k for k,v in rulegroup.items() if k != v),
-            rules='\n'.join('{}: {}'.format(k,v)
-                            for k,v in sorted(rulegroup.items())
-                            if k != v),
-            htmlrules='\n'.join(HTML_ALIAS_RULE.format(k[:-2],v[:-2])
-                                for k,v in sorted(rulegroup.items())
-                                if k != v),
-            conditional=conditional)
-        for conditional,rulegroup in sorted(rules.items())
-        ) + FOOTER.format(files=mjoin(sorted(files)))
-
-if __name__ == '__main__':
-    rules = create_rules(sys.argv[1:])
-    files = (xml(file) for file in sys.argv[1:])
-    print(make_makefile(rules, files), end='')
diff --git a/tools/make-directive-index.py b/tools/make-directive-index.py
new file mode 100755
index 0000000..2ff304f
--- /dev/null
+++ b/tools/make-directive-index.py
@@ -0,0 +1,320 @@
+#  -*- Mode: python; coding: utf-8; indent-tabs-mode: nil -*- */
+#
+#  This file is part of systemd.
+#
+#  Copyright 2012-2013 Zbigniew Jędrzejewski-Szmek
+#
+#  systemd is free software; you can redistribute it and/or modify it
+#  under the terms of the GNU Lesser General Public License as published by
+#  the Free Software Foundation; either version 2.1 of the License, or
+#  (at your option) any later version.
+#
+#  systemd is distributed in the hope that it will be useful, but
+#  WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+#  Lesser General Public License for more details.
+#
+#  You should have received a copy of the GNU Lesser General Public License
+#  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+
+import sys
+import collections
+import re
+from xml_helper import *
+from copy import deepcopy
+
+TEMPLATE = '''\
+<refentry id="systemd.directives" conditional="HAVE_PYTHON">
+
+        <refentryinfo>
+                <title>systemd.directives</title>
+                <productname>systemd</productname>
+
+                <authorgroup>
+                        <author>
+                                <contrib>Developer</contrib>
+                                <firstname>Zbigniew</firstname>
+                                <surname>Jędrzejewski-Szmek</surname>
+                                <email>zbyszek at in.waw.pl</email>
+                        </author>
+                </authorgroup>
+        </refentryinfo>
+
+        <refmeta>
+                <refentrytitle>systemd.directives</refentrytitle>
+                <manvolnum>7</manvolnum>
+        </refmeta>
+
+        <refnamediv>
+                <refname>systemd.directives</refname>
+                <refpurpose>Index of configuration directives</refpurpose>
+        </refnamediv>
+
+        <refsect1>
+                <title>Unit directives</title>
+
+                <para>Directives for configuring units, used in unit
+                files.</para>
+
+                <variablelist id='unit-directives' />
+        </refsect1>
+
+        <refsect1>
+                <title>Options on the kernel command line</title>
+
+                <para>Kernel boot options for configuring the behaviour of the
+                systemd process.</para>
+
+                <variablelist id='kernel-commandline-options' />
+        </refsect1>
+
+        <refsect1>
+                <title>Environment variables</title>
+
+                <para>Environment variables understood by the systemd
+                manager and other programs.</para>
+
+                <variablelist id='environment-variables' />
+        </refsect1>
+
+        <refsect1>
+                <title>UDEV directives</title>
+
+                <para>Directives for configuring systemd units through the
+                udev database.</para>
+
+                <variablelist id='udev-directives' />
+        </refsect1>
+
+        <refsect1>
+                <title>Network directives</title>
+
+                <para>Directives for configuring network links through the
+                net-setup-link udev builtin and networks through
+                systemd-networkd.</para>
+
+                <variablelist id='network-directives' />
+        </refsect1>
+
+        <refsect1>
+                <title>Journal fields</title>
+
+                <para>Fields in the journal events with a well known meaning.</para>
+
+                <variablelist id='journal-directives' />
+        </refsect1>
+
+        <refsect1>
+                <title>PAM configuration directives</title>
+
+                <para>Directives for configuring PAM behaviour.</para>
+
+                <variablelist id='pam-directives' />
+        </refsect1>
+
+        <refsect1>
+                <title>crypttab options</title>
+
+                <para>Options which influence mounted filesystems and
+                encrypted volumes.</para>
+
+                <variablelist id='crypttab-options' />
+        </refsect1>
+
+        <refsect1>
+                <title>System manager directives</title>
+
+                <para>Directives for configuring the behaviour of the
+                systemd process.</para>
+
+                <variablelist id='systemd-directives' />
+        </refsect1>
+
+        <refsect1>
+                <title>bootchart.conf directives</title>
+
+                <para>Directives for configuring the behaviour of the
+                systemd-bootchart process.</para>
+
+                <variablelist id='bootchart-directives' />
+        </refsect1>
+
+        <refsect1>
+                <title>command-line options</title>
+
+                <para>Command-line options accepted by programs in the
+                systemd suite.</para>
+
+                <variablelist id='options' />
+        </refsect1>
+
+        <refsect1>
+                <title>Constants</title>
+
+                <para>Various constant used and/or defined by systemd.</para>
+
+                <variablelist id='constants' />
+        </refsect1>
+
+        <refsect1>
+                <title>Miscellaneous options and directives</title>
+
+                <para>Other configuration elements which don't fit in
+                any of the above groups.</para>
+
+                <variablelist id='miscellaneous' />
+        </refsect1>
+
+        <refsect1>
+                <title>Files and directories</title>
+
+                <para>Paths and file names referred to in the
+                documentation.</para>
+
+                <variablelist id='filenames' />
+        </refsect1>
+
+        <refsect1>
+                <title>Colophon</title>
+                <para id='colophon' />
+        </refsect1>
+</refentry>
+'''
+
+COLOPHON = '''\
+This index contains {count} entries in {sections} sections,
+referring to {pages} individual manual pages.
+'''
+
+def _extract_directives(directive_groups, formatting, page):
+    t = xml_parse(page)
+    section = t.find('./refmeta/manvolnum').text
+    pagename = t.find('./refmeta/refentrytitle').text
+
+    storopt = directive_groups['options']
+    for variablelist in t.iterfind('.//variablelist'):
+        klass = variablelist.attrib.get('class')
+        storvar = directive_groups[klass or 'miscellaneous']
+        # <option>s go in OPTIONS, unless class is specified
+        for xpath, stor in (('./varlistentry/term/varname', storvar),
+                            ('./varlistentry/term/option',
+                             storvar if klass else storopt)):
+            for name in variablelist.iterfind(xpath):
+                text = re.sub(r'([= ]).*', r'\1', name.text).rstrip()
+                stor[text].append((pagename, section))
+                if text not in formatting:
+                    # use element as formatted display
+                    if name.text[-1] in '= ':
+                        name.clear()
+                    else:
+                        name.tail = ''
+                    name.text = text
+                    formatting[text] = name
+
+    storfile = directive_groups['filenames']
+    for xpath, absolute_only in (('.//refsynopsisdiv//filename', False),
+                                 ('.//refsynopsisdiv//command', False),
+                                 ('.//filename', True)):
+        for name in t.iterfind(xpath):
+            if absolute_only and not (name.text and name.text.startswith('/')):
+                continue
+            if name.attrib.get('noindex'):
+                continue
+            name.tail = ''
+            if name.text:
+                if name.text.endswith('*'):
+                    name.text = name.text[:-1]
+                if not name.text.startswith('.'):
+                    text = name.text.partition(' ')[0]
+                    if text != name.text:
+                        name.clear()
+                        name.text = text
+                    if text.endswith('/'):
+                        text = text[:-1]
+                    storfile[text].append((pagename, section))
+                    if text not in formatting:
+                        # use element as formatted display
+                        formatting[text] = name
+            else:
+                text = ' '.join(name.itertext())
+                storfile[text].append((pagename, section))
+                formatting[text] = name
+
+    storfile = directive_groups['constants']
+    for name in t.iterfind('.//constant'):
+        if name.attrib.get('noindex'):
+            continue
+        name.tail = ''
+        if name.text.startswith('('): # a cast, strip it
+            name.text = name.text.partition(' ')[2]
+        storfile[name.text].append((pagename, section))
+        formatting[name.text] = name
+
+def _make_section(template, name, directives, formatting):
+    varlist = template.find(".//*[@id='{}']".format(name))
+    for varname, manpages in sorted(directives.items()):
+        entry = tree.SubElement(varlist, 'varlistentry')
+        term = tree.SubElement(entry, 'term')
+        display = deepcopy(formatting[varname])
+        term.append(display)
+
+        para = tree.SubElement(tree.SubElement(entry, 'listitem'), 'para')
+
+        b = None
+        for manpage, manvolume in sorted(set(manpages)):
+            if b is not None:
+                b.tail = ', '
+            b = tree.SubElement(para, 'citerefentry')
+            c = tree.SubElement(b, 'refentrytitle')
+            c.text = manpage
+            d = tree.SubElement(b, 'manvolnum')
+            d.text = manvolume
+        entry.tail = '\n\n'
+
+def _make_colophon(template, groups):
+    count = 0
+    pages = set()
+    for group in groups:
+        count += len(group)
+        for pagelist in group.values():
+            pages |= set(pagelist)
+
+    para = template.find(".//para[@id='colophon']")
+    para.text = COLOPHON.format(count=count,
+                                sections=len(groups),
+                                pages=len(pages))
+
+def _make_page(template, directive_groups, formatting):
+    """Create an XML tree from directive_groups.
+
+    directive_groups = {
+       'class': {'variable': [('manpage', 'manvolume'), ...],
+                 'variable2': ...},
+       ...
+    }
+    """
+    for name, directives in directive_groups.items():
+        _make_section(template, name, directives, formatting)
+
+    _make_colophon(template, directive_groups.values())
+
+    return template
+
+def make_page(*xml_files):
+    "Extract directives from xml_files and return XML index tree."
+    template = tree.fromstring(TEMPLATE)
+    names = [vl.get('id') for vl in template.iterfind('.//variablelist')]
+    directive_groups = {name:collections.defaultdict(list)
+                        for name in names}
+    formatting = {}
+    for page in xml_files:
+        try:
+            _extract_directives(directive_groups, formatting, page)
+        except Exception:
+            raise ValueError("failed to process " + page)
+
+    return _make_page(template, directive_groups, formatting)
+
+if __name__ == '__main__':
+    with open(sys.argv[1], 'wb') as f:
+        f.write(xml_print(make_page(*sys.argv[2:])))
diff --git a/tools/make-man-index.py b/tools/make-man-index.py
new file mode 100755
index 0000000..74a47b8
--- /dev/null
+++ b/tools/make-man-index.py
@@ -0,0 +1,136 @@
+#  -*- Mode: python; coding: utf-8; indent-tabs-mode: nil -*- */
+#
+#  This file is part of systemd.
+#
+#  Copyright 2012 Lennart Poettering
+#  Copyright 2013 Zbigniew Jędrzejewski-Szmek
+#
+#  systemd is free software; you can redistribute it and/or modify it
+#  under the terms of the GNU Lesser General Public License as published by
+#  the Free Software Foundation; either version 2.1 of the License, or
+#  (at your option) any later version.
+#
+#  systemd is distributed in the hope that it will be useful, but
+#  WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+#  Lesser General Public License for more details.
+#
+#  You should have received a copy of the GNU Lesser General Public License
+#  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+
+import collections
+import sys
+import re
+from xml_helper import *
+
+MDASH = ' — ' if sys.version_info.major >= 3 else ' -- '
+
+TEMPLATE = '''\
+<refentry id="systemd.index" conditional="HAVE_PYTHON">
+
+  <refentryinfo>
+    <title>systemd.index</title>
+    <productname>systemd</productname>
+
+    <authorgroup>
+      <author>
+        <contrib>Developer</contrib>
+        <firstname>Lennart</firstname>
+        <surname>Poettering</surname>
+        <email>lennart at poettering.net</email>
+      </author>
+    </authorgroup>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>systemd.index</refentrytitle>
+    <manvolnum>7</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname>systemd.index</refname>
+    <refpurpose>List all manpages from the systemd project</refpurpose>
+  </refnamediv>
+</refentry>
+'''
+
+SUMMARY = '''\
+  <refsect1>
+    <title>See Also</title>
+    <para>
+      <citerefentry><refentrytitle>systemd.directives</refentrytitle><manvolnum>7</manvolnum></citerefentry>
+    </para>
+
+    <para id='counts' />
+  </refsect1>
+'''
+
+COUNTS = '\
+This index contains {count} entries, referring to {pages} individual manual pages.'
+
+
+def check_id(page, t):
+    id = t.getroot().get('id')
+    if not re.search('/' + id + '[.]', page):
+        raise ValueError("id='{}' is not the same as page name '{}'".format(id, page))
+
+def make_index(pages):
+    index = collections.defaultdict(list)
+    for p in pages:
+        t = xml_parse(p)
+        check_id(p, t)
+        section = t.find('./refmeta/manvolnum').text
+        refname = t.find('./refnamediv/refname').text
+        purpose = ' '.join(t.find('./refnamediv/refpurpose').text.split())
+        for f in t.findall('./refnamediv/refname'):
+            infos = (f.text, section, purpose, refname)
+            index[f.text[0].upper()].append(infos)
+    return index
+
+def add_letter(template, letter, pages):
+    refsect1 = tree.SubElement(template, 'refsect1')
+    title = tree.SubElement(refsect1, 'title')
+    title.text = letter
+    para = tree.SubElement(refsect1, 'para')
+    for info in sorted(pages, key=lambda info: str.lower(info[0])):
+        refname, section, purpose, realname = info
+
+        b = tree.SubElement(para, 'citerefentry')
+        c = tree.SubElement(b, 'refentrytitle')
+        c.text = refname
+        d = tree.SubElement(b, 'manvolnum')
+        d.text = section
+
+        b.tail = MDASH + purpose # + ' (' + p + ')'
+
+        tree.SubElement(para, 'sbr')
+
+def add_summary(template, indexpages):
+    count = 0
+    pages = set()
+    for group in indexpages:
+        count += len(group)
+        for info in group:
+            refname, section, purpose, realname = info
+            pages.add((realname, section))
+
+    refsect1 = tree.fromstring(SUMMARY)
+    template.append(refsect1)
+
+    para = template.find(".//para[@id='counts']")
+    para.text = COUNTS.format(count=count, pages=len(pages))
+
+def make_page(*xml_files):
+    template = tree.fromstring(TEMPLATE)
+    index = make_index(xml_files)
+
+    for letter in sorted(index):
+        add_letter(template, letter, index[letter])
+
+    add_summary(template, index.values())
+
+    return template
+
+if __name__ == '__main__':
+    with open(sys.argv[1], 'wb') as f:
+        f.write(xml_print(make_page(*sys.argv[2:])))
diff --git a/tools/make-man-rules.py b/tools/make-man-rules.py
new file mode 100644
index 0000000..0d1ca24
--- /dev/null
+++ b/tools/make-man-rules.py
@@ -0,0 +1,113 @@
+#  -*- Mode: python; coding: utf-8; indent-tabs-mode: nil -*- */
+#
+#  This file is part of systemd.
+#
+#  Copyright 2013 Zbigniew Jędrzejewski-Szmek
+#
+#  systemd is free software; you can redistribute it and/or modify it
+#  under the terms of the GNU Lesser General Public License as published by
+#  the Free Software Foundation; either version 2.1 of the License, or
+#  (at your option) any later version.
+#
+#  systemd is distributed in the hope that it will be useful, but
+#  WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+#  Lesser General Public License for more details.
+#
+#  You should have received a copy of the GNU Lesser General Public License
+#  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+
+from __future__ import print_function
+import collections
+import sys
+import os.path
+from xml_helper import *
+
+SECTION = '''\
+MANPAGES += \\
+	{manpages}
+MANPAGES_ALIAS += \\
+	{aliases}
+{rules}
+{htmlrules}
+'''
+
+CONDITIONAL = '''\
+if {conditional}
+''' \
++ SECTION + \
+'''\
+endif
+'''
+
+HEADER = '''\
+# Do not edit. Generated by make-man-rules.py.
+# Regenerate with 'make all update-man-list'.
+
+'''
+
+HTML_ALIAS_RULE = '''\
+{}.html: {}.html
+	$(html-alias)
+'''
+
+FOOTER = '''\
+
+EXTRA_DIST += \\
+       {files}
+'''
+
+def man(page, number):
+    return 'man/{}.{}'.format(page, number)
+
+def xml(file):
+    return 'man/{}'.format(os.path.basename(file))
+
+def add_rules(rules, name):
+    xml = xml_parse(name)
+    # print('parsing {}'.format(name), file=sys.stderr)
+    conditional = xml.getroot().get('conditional') or ''
+    rulegroup = rules[conditional]
+    refmeta = xml.find('./refmeta')
+    title = refmeta.find('./refentrytitle').text
+    number = refmeta.find('./manvolnum').text
+    refnames = xml.findall('./refnamediv/refname')
+    target = man(refnames[0].text, number)
+    if title != refnames[0].text:
+        raise ValueError('refmeta and refnamediv disagree: ' + name)
+    for refname in refnames:
+        assert all(refname not in group
+                   for group in rules.values()), "duplicate page name"
+        alias = man(refname.text, number)
+        rulegroup[alias] = target
+        # print('{} => {} [{}]'.format(alias, target, conditional), file=sys.stderr)
+
+def create_rules(xml_files):
+    " {conditional => {alias-name => source-name}} "
+    rules = collections.defaultdict(dict)
+    for name in xml_files:
+        add_rules(rules, name)
+    return rules
+
+def mjoin(files):
+    return ' \\\n\t'.join(sorted(files) or '#')
+
+def make_makefile(rules, files):
+    return HEADER + '\n'.join(
+        (CONDITIONAL if conditional else SECTION).format(
+            manpages=mjoin(set(rulegroup.values())),
+            aliases=mjoin(k for k,v in rulegroup.items() if k != v),
+            rules='\n'.join('{}: {}'.format(k,v)
+                            for k,v in sorted(rulegroup.items())
+                            if k != v),
+            htmlrules='\n'.join(HTML_ALIAS_RULE.format(k[:-2],v[:-2])
+                                for k,v in sorted(rulegroup.items())
+                                if k != v),
+            conditional=conditional)
+        for conditional,rulegroup in sorted(rules.items())
+        ) + FOOTER.format(files=mjoin(sorted(files)))
+
+if __name__ == '__main__':
+    rules = create_rules(sys.argv[1:])
+    files = (xml(file) for file in sys.argv[1:])
+    print(make_makefile(rules, files), end='')
diff --git a/tools/xml_helper.py b/tools/xml_helper.py
new file mode 100644
index 0000000..08e226f
--- /dev/null
+++ b/tools/xml_helper.py
@@ -0,0 +1,41 @@
+#  -*- Mode: python; coding: utf-8; indent-tabs-mode: nil -*- */
+#
+#  This file is part of systemd.
+#
+#  Copyright 2012-2013 Zbigniew Jędrzejewski-Szmek
+#
+#  systemd is free software; you can redistribute it and/or modify it
+#  under the terms of the GNU Lesser General Public License as published by
+#  the Free Software Foundation; either version 2.1 of the License, or
+#  (at your option) any later version.
+#
+#  systemd is distributed in the hope that it will be useful, but
+#  WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+#  Lesser General Public License for more details.
+#
+#  You should have received a copy of the GNU Lesser General Public License
+#  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+
+try:
+    from lxml import etree as tree
+
+    class CustomResolver(tree.Resolver):
+        def resolve(self, url, id, context):
+            if 'custom-entities.ent' in url:
+                return self.resolve_filename('man/custom-entities.ent', context)
+
+    _parser = tree.XMLParser()
+    _parser.resolvers.add(CustomResolver())
+    xml_parse = lambda page: tree.parse(page, _parser)
+    xml_print = lambda xml: tree.tostring(xml, pretty_print=True,
+                                          encoding='utf-8')
+except ImportError:
+    import xml.etree.ElementTree as tree
+    import re as _re
+    import io as _io
+
+    def xml_parse(page):
+        s = _re.sub(b'&[a-zA-Z0-9_]+;', b'', open(page, 'rb').read())
+        return tree.parse(_io.BytesIO(s))
+    xml_print = lambda xml: tree.tostring(xml, encoding='utf-8')
diff --git a/xml_helper.py b/xml_helper.py
deleted file mode 100644
index 08e226f..0000000
--- a/xml_helper.py
+++ /dev/null
@@ -1,41 +0,0 @@
-#  -*- Mode: python; coding: utf-8; indent-tabs-mode: nil -*- */
-#
-#  This file is part of systemd.
-#
-#  Copyright 2012-2013 Zbigniew Jędrzejewski-Szmek
-#
-#  systemd is free software; you can redistribute it and/or modify it
-#  under the terms of the GNU Lesser General Public License as published by
-#  the Free Software Foundation; either version 2.1 of the License, or
-#  (at your option) any later version.
-#
-#  systemd is distributed in the hope that it will be useful, but
-#  WITHOUT ANY WARRANTY; without even the implied warranty of
-#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-#  Lesser General Public License for more details.
-#
-#  You should have received a copy of the GNU Lesser General Public License
-#  along with systemd; If not, see <http://www.gnu.org/licenses/>.
-
-try:
-    from lxml import etree as tree
-
-    class CustomResolver(tree.Resolver):
-        def resolve(self, url, id, context):
-            if 'custom-entities.ent' in url:
-                return self.resolve_filename('man/custom-entities.ent', context)
-
-    _parser = tree.XMLParser()
-    _parser.resolvers.add(CustomResolver())
-    xml_parse = lambda page: tree.parse(page, _parser)
-    xml_print = lambda xml: tree.tostring(xml, pretty_print=True,
-                                          encoding='utf-8')
-except ImportError:
-    import xml.etree.ElementTree as tree
-    import re as _re
-    import io as _io
-
-    def xml_parse(page):
-        s = _re.sub(b'&[a-zA-Z0-9_]+;', b'', open(page, 'rb').read())
-        return tree.parse(_io.BytesIO(s))
-    xml_print = lambda xml: tree.tostring(xml, encoding='utf-8')
-- 
1.8.5.3



More information about the systemd-devel mailing list