Commit 5ce58234 authored by Michael Hanselmann's avatar Michael Hanselmann
Browse files

Link man pages in documentation

This patch depends on “Option to include man pages in documentation”. In
the documentation build including man pages, all “:manpage:`…`”
references are converted to links. For man pages not provided by Ganeti,
Sphinx' standard formatting is used.

A small dance is necessary to hook into Sphinx' processing of man page
roles and to generate automatically resolved links. The code converts
“:manpage:`…`” for known man pages to the data structure equivalent of
“:doc:`$name($section) <man-$name>`”. Additionally it checks the section
numbers and formatting of references (in all builds).
Signed-off-by: default avatarMichael Hanselmann <>
Reviewed-by: default avatarGuido Trotter <>
parent a7f0953a
......@@ -929,7 +929,10 @@ man_MANS = \
man/htools.1 \
manrst = $(patsubst %.1,%.rst,$(patsubst %.7,%.rst,$(patsubst %.8,%.rst,$(man_MANS))))
# Remove extensions from all filenames in man_MANS
mannoext = $(patsubst %.1,%,$(patsubst %.7,%,$(patsubst %.8,%,$(man_MANS))))
manrst = $(patsubst %,%.rst,$(mannoext))
manhtml = $(patsubst %.rst,%.html,$(manrst))
mangen = $(patsubst %.rst,%.gen,$(manrst))
maninput = \
......@@ -1407,6 +1410,13 @@ lib/ Makefile | stamp-directories
## Write dictionary with man page name as the key and the section number as the
## value
echo "MAN_PAGES = {"; \
for i in $(notdir $(man_MANS)); do \
echo "$$i" | sed -re 's/^(.*)\.([0-9]+)$$/ "\1": \2,/g'; \
done; \
echo "}"; \
} > $@
lib/ Makefile vcs-version | stamp-directories
......@@ -23,17 +23,29 @@
import re
from cStringIO import StringIO
import docutils.statemachine
import docutils.nodes
import docutils.utils
import docutils.parsers.rst
import sphinx.errors
import sphinx.util.compat
import sphinx.roles
import sphinx.addnodes
s_compat = sphinx.util.compat
# Access to a protected member of a client class
# pylint: disable=W0212
orig_manpage_role = docutils.parsers.rst.roles._roles["manpage"]
except (AttributeError, ValueError, KeyError), err:
# Normally the "manpage" role is registered by sphinx/
raise Exception("Can't find reST role named 'manpage': %s" % err)
from ganeti import constants
from ganeti import compat
from ganeti import errors
......@@ -42,10 +54,21 @@ from ganeti import opcodes
from ganeti import ht
from ganeti import rapi
from ganeti import luxi
from ganeti import _autoconf
import ganeti.rapi.rlib2 # pylint: disable=W0611
#: Regular expression for man page names
_MAN_RE = re.compile(r"^(?P<name>[-\w_]+)\((?P<section>\d+)\)$")
class ReSTError(Exception):
"""Custom class for generating errors in Sphinx.
def _GetCommonParamNames():
"""Builds a list of parameters common to all opcodes.
......@@ -310,14 +333,106 @@ def BuildValuesDoc(values):
yield " %s" % doc
# TODO: Implement Sphinx directive for query fields
def _ManPageNodeClass(*args, **kwargs):
"""Generates a pending XRef like a ":doc:`...`" reference.
# Type for sphinx/
kwargs["reftype"] = "doc"
# Force custom title
kwargs["refexplicit"] = True
return sphinx.addnodes.pending_xref(*args, **kwargs)
class _ManPageXRefRole(sphinx.roles.XRefRole):
def __init__(self):
"""Initializes this class.
sphinx.roles.XRefRole.__init__(self, nodeclass=_ManPageNodeClass,
assert not hasattr(self, "converted"), \
"Sphinx base class gained an attribute named 'converted'"
self.converted = None
def process_link(self, env, refnode, has_explicit_title, title, target):
"""Specialization for man page links.
if has_explicit_title:
raise ReSTError("Setting explicit title is not allowed for man pages")
# Check format and extract name and section
m = _MAN_RE.match(title)
if not m:
raise ReSTError("Man page reference '%s' does not match regular"
" expression '%s'" % (title, _MAN_RE.pattern))
name ="name")
section = int("section"))
wanted_section = _autoconf.MAN_PAGES.get(name, None)
if not (wanted_section is None or wanted_section == section):
raise ReSTError("Referenced man page '%s' has section number %s, but the"
" reference uses section %s" %
(name, wanted_section, section))
self.converted = bool(wanted_section is not None and
if self.converted:
# Create link to known man page
return (title, "man-%s" % name)
# No changes
return (title, target)
def _ManPageRole(typ, rawtext, text, lineno, inliner, # pylint: disable=W0102
options={}, content=[]):
"""Custom role for man page references.
Converts man pages to links if enabled during the build.
xref = _ManPageXRefRole()
assert ht.TNone(xref.converted)
# Check if it's a known man page
result = xref(typ, rawtext, text, lineno, inliner,
options=options, content=content)
except ReSTError, err:
msg = inliner.reporter.error(str(err), line=lineno)
return ([inliner.problematic(rawtext, rawtext, msg)], [msg])
assert ht.TBool(xref.converted)
# Return if the conversion was successful (i.e. the man page was known and
# conversion was enabled)
if xref.converted:
return result
# Fallback if man page links are disabled or an unknown page is referenced
return orig_manpage_role(typ, rawtext, text, lineno, inliner,
options=options, content=content)
def setup(app):
"""Sphinx extension callback.
# TODO: Implement Sphinx directive for query fields
app.add_directive("opcode_params", OpcodeParams)
app.add_directive("opcode_result", OpcodeResult)
app.add_directive("pyassert", PythonAssert)
app.add_role("pyeval", PythonEvalRole)
app.add_config_value("enable_manpages", False, True)
app.add_role("manpage", _ManPageRole)
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment