Revision 5ce58234

b/Makefile.am
929 929
	man/htools.1 \
930 930
	man/mon-collector.7
931 931

  
932
manrst = $(patsubst %.1,%.rst,$(patsubst %.7,%.rst,$(patsubst %.8,%.rst,$(man_MANS))))
932
# Remove extensions from all filenames in man_MANS
933
mannoext = $(patsubst %.1,%,$(patsubst %.7,%,$(patsubst %.8,%,$(man_MANS))))
934

  
935
manrst = $(patsubst %,%.rst,$(mannoext))
933 936
manhtml = $(patsubst %.rst,%.html,$(manrst))
934 937
mangen = $(patsubst %.rst,%.gen,$(manrst))
935 938
maninput = \
......
1407 1410
	  echo "ENABLE_SPLIT_QUERY = $(ENABLE_SPLIT_QUERY)"; \
1408 1411
	  echo "ENABLE_RESTRICTED_COMMANDS = $(ENABLE_RESTRICTED_COMMANDS)"; \
1409 1412
	  echo "ENABLE_MONITORING = $(ENABLE_MONITORING)"; \
1413
## Write dictionary with man page name as the key and the section number as the
1414
## value
1415
	  echo "MAN_PAGES = {"; \
1416
	  for i in $(notdir $(man_MANS)); do \
1417
	    echo "$$i" | sed -re 's/^(.*)\.([0-9]+)$$/  "\1": \2,/g'; \
1418
	  done; \
1419
	  echo "}"; \
1410 1420
	} > $@
1411 1421

  
1412 1422
lib/_vcsversion.py: Makefile vcs-version | stamp-directories
b/lib/build/sphinx_ext.py
23 23

  
24 24
"""
25 25

  
26
import re
26 27
from cStringIO import StringIO
27 28

  
28 29
import docutils.statemachine
29 30
import docutils.nodes
30 31
import docutils.utils
32
import docutils.parsers.rst
31 33

  
32 34
import sphinx.errors
33 35
import sphinx.util.compat
36
import sphinx.roles
37
import sphinx.addnodes
34 38

  
35 39
s_compat = sphinx.util.compat
36 40

  
41
try:
42
  # Access to a protected member of a client class
43
  # pylint: disable=W0212
44
  orig_manpage_role = docutils.parsers.rst.roles._roles["manpage"]
45
except (AttributeError, ValueError, KeyError), err:
46
  # Normally the "manpage" role is registered by sphinx/roles.py
47
  raise Exception("Can't find reST role named 'manpage': %s" % err)
48

  
37 49
from ganeti import constants
38 50
from ganeti import compat
39 51
from ganeti import errors
......
42 54
from ganeti import ht
43 55
from ganeti import rapi
44 56
from ganeti import luxi
57
from ganeti import _autoconf
45 58

  
46 59
import ganeti.rapi.rlib2 # pylint: disable=W0611
47 60

  
48 61

  
62
#: Regular expression for man page names
63
_MAN_RE = re.compile(r"^(?P<name>[-\w_]+)\((?P<section>\d+)\)$")
64

  
65

  
66
class ReSTError(Exception):
67
  """Custom class for generating errors in Sphinx.
68

  
69
  """
70

  
71

  
49 72
def _GetCommonParamNames():
50 73
  """Builds a list of parameters common to all opcodes.
51 74

  
......
310 333
    yield "  %s" % doc
311 334

  
312 335

  
313
# TODO: Implement Sphinx directive for query fields
336
def _ManPageNodeClass(*args, **kwargs):
337
  """Generates a pending XRef like a ":doc:`...`" reference.
338

  
339
  """
340
  # Type for sphinx/environment.py:BuildEnvironment.resolve_references
341
  kwargs["reftype"] = "doc"
342

  
343
  # Force custom title
344
  kwargs["refexplicit"] = True
345

  
346
  return sphinx.addnodes.pending_xref(*args, **kwargs)
347

  
348

  
349
class _ManPageXRefRole(sphinx.roles.XRefRole):
350
  def __init__(self):
351
    """Initializes this class.
352

  
353
    """
354
    sphinx.roles.XRefRole.__init__(self, nodeclass=_ManPageNodeClass,
355
                                   warn_dangling=True)
356

  
357
    assert not hasattr(self, "converted"), \
358
      "Sphinx base class gained an attribute named 'converted'"
359

  
360
    self.converted = None
361

  
362
  def process_link(self, env, refnode, has_explicit_title, title, target):
363
    """Specialization for man page links.
364

  
365
    """
366
    if has_explicit_title:
367
      raise ReSTError("Setting explicit title is not allowed for man pages")
368

  
369
    # Check format and extract name and section
370
    m = _MAN_RE.match(title)
371
    if not m:
372
      raise ReSTError("Man page reference '%s' does not match regular"
373
                      " expression '%s'" % (title, _MAN_RE.pattern))
374

  
375
    name = m.group("name")
376
    section = int(m.group("section"))
377

  
378
    wanted_section = _autoconf.MAN_PAGES.get(name, None)
379

  
380
    if not (wanted_section is None or wanted_section == section):
381
      raise ReSTError("Referenced man page '%s' has section number %s, but the"
382
                      " reference uses section %s" %
383
                      (name, wanted_section, section))
384

  
385
    self.converted = bool(wanted_section is not None and
386
                          env.app.config.enable_manpages)
387

  
388
    if self.converted:
389
      # Create link to known man page
390
      return (title, "man-%s" % name)
391
    else:
392
      # No changes
393
      return (title, target)
394

  
395

  
396
def _ManPageRole(typ, rawtext, text, lineno, inliner, # pylint: disable=W0102
397
                 options={}, content=[]):
398
  """Custom role for man page references.
399

  
400
  Converts man pages to links if enabled during the build.
401

  
402
  """
403
  xref = _ManPageXRefRole()
404

  
405
  assert ht.TNone(xref.converted)
406

  
407
  # Check if it's a known man page
408
  try:
409
    result = xref(typ, rawtext, text, lineno, inliner,
410
                  options=options, content=content)
411
  except ReSTError, err:
412
    msg = inliner.reporter.error(str(err), line=lineno)
413
    return ([inliner.problematic(rawtext, rawtext, msg)], [msg])
414

  
415
  assert ht.TBool(xref.converted)
416

  
417
  # Return if the conversion was successful (i.e. the man page was known and
418
  # conversion was enabled)
419
  if xref.converted:
420
    return result
421

  
422
  # Fallback if man page links are disabled or an unknown page is referenced
423
  return orig_manpage_role(typ, rawtext, text, lineno, inliner,
424
                           options=options, content=content)
314 425

  
315 426

  
316 427
def setup(app):
317 428
  """Sphinx extension callback.
318 429

  
319 430
  """
431
  # TODO: Implement Sphinx directive for query fields
320 432
  app.add_directive("opcode_params", OpcodeParams)
321 433
  app.add_directive("opcode_result", OpcodeResult)
322 434
  app.add_directive("pyassert", PythonAssert)
323 435
  app.add_role("pyeval", PythonEvalRole)
436

  
437
  app.add_config_value("enable_manpages", False, True)
438
  app.add_role("manpage", _ManPageRole)

Also available in: Unified diff