kernel_feat.py 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. # coding=utf-8
  2. # SPDX-License-Identifier: GPL-2.0
  3. #
  4. u"""
  5. kernel-feat
  6. ~~~~~~~~~~~
  7. Implementation of the ``kernel-feat`` reST-directive.
  8. :copyright: Copyright (C) 2016 Markus Heiser
  9. :copyright: Copyright (C) 2016-2019 Mauro Carvalho Chehab
  10. :maintained-by: Mauro Carvalho Chehab <[email protected]>
  11. :license: GPL Version 2, June 1991 see Linux/COPYING for details.
  12. The ``kernel-feat`` (:py:class:`KernelFeat`) directive calls the
  13. scripts/get_feat.pl script to parse the Kernel ABI files.
  14. Overview of directive's argument and options.
  15. .. code-block:: rst
  16. .. kernel-feat:: <ABI directory location>
  17. :debug:
  18. The argument ``<ABI directory location>`` is required. It contains the
  19. location of the ABI files to be parsed.
  20. ``debug``
  21. Inserts a code-block with the *raw* reST. Sometimes it is helpful to see
  22. what reST is generated.
  23. """
  24. import codecs
  25. import os
  26. import re
  27. import subprocess
  28. import sys
  29. from os import path
  30. from docutils import nodes, statemachine
  31. from docutils.statemachine import ViewList
  32. from docutils.parsers.rst import directives, Directive
  33. from docutils.utils.error_reporting import ErrorString
  34. from sphinx.util.docutils import switch_source_input
  35. __version__ = '1.0'
  36. def setup(app):
  37. app.add_directive("kernel-feat", KernelFeat)
  38. return dict(
  39. version = __version__
  40. , parallel_read_safe = True
  41. , parallel_write_safe = True
  42. )
  43. class KernelFeat(Directive):
  44. u"""KernelFeat (``kernel-feat``) directive"""
  45. required_arguments = 1
  46. optional_arguments = 2
  47. has_content = False
  48. final_argument_whitespace = True
  49. option_spec = {
  50. "debug" : directives.flag
  51. }
  52. def warn(self, message, **replace):
  53. replace["fname"] = self.state.document.current_source
  54. replace["line_no"] = replace.get("line_no", self.lineno)
  55. message = ("%(fname)s:%(line_no)s: [kernel-feat WARN] : " + message) % replace
  56. self.state.document.settings.env.app.warn(message, prefix="")
  57. def run(self):
  58. doc = self.state.document
  59. if not doc.settings.file_insertion_enabled:
  60. raise self.warning("docutils: file insertion disabled")
  61. env = doc.settings.env
  62. cwd = path.dirname(doc.current_source)
  63. cmd = "get_feat.pl rest --enable-fname --dir "
  64. cmd += self.arguments[0]
  65. if len(self.arguments) > 1:
  66. cmd += " --arch " + self.arguments[1]
  67. srctree = path.abspath(os.environ["srctree"])
  68. fname = cmd
  69. # extend PATH with $(srctree)/scripts
  70. path_env = os.pathsep.join([
  71. srctree + os.sep + "scripts",
  72. os.environ["PATH"]
  73. ])
  74. shell_env = os.environ.copy()
  75. shell_env["PATH"] = path_env
  76. shell_env["srctree"] = srctree
  77. lines = self.runCmd(cmd, shell=True, cwd=cwd, env=shell_env)
  78. line_regex = re.compile("^\.\. FILE (\S+)$")
  79. out_lines = ""
  80. for line in lines.split("\n"):
  81. match = line_regex.search(line)
  82. if match:
  83. fname = match.group(1)
  84. # Add the file to Sphinx build dependencies
  85. env.note_dependency(os.path.abspath(fname))
  86. else:
  87. out_lines += line + "\n"
  88. nodeList = self.nestedParse(out_lines, fname)
  89. return nodeList
  90. def runCmd(self, cmd, **kwargs):
  91. u"""Run command ``cmd`` and return its stdout as unicode."""
  92. try:
  93. proc = subprocess.Popen(
  94. cmd
  95. , stdout = subprocess.PIPE
  96. , stderr = subprocess.PIPE
  97. , **kwargs
  98. )
  99. out, err = proc.communicate()
  100. out, err = codecs.decode(out, 'utf-8'), codecs.decode(err, 'utf-8')
  101. if proc.returncode != 0:
  102. raise self.severe(
  103. u"command '%s' failed with return code %d"
  104. % (cmd, proc.returncode)
  105. )
  106. except OSError as exc:
  107. raise self.severe(u"problems with '%s' directive: %s."
  108. % (self.name, ErrorString(exc)))
  109. return out
  110. def nestedParse(self, lines, fname):
  111. content = ViewList()
  112. node = nodes.section()
  113. if "debug" in self.options:
  114. code_block = "\n\n.. code-block:: rst\n :linenos:\n"
  115. for l in lines.split("\n"):
  116. code_block += "\n " + l
  117. lines = code_block + "\n\n"
  118. for c, l in enumerate(lines.split("\n")):
  119. content.append(l, fname, c)
  120. buf = self.state.memo.title_styles, self.state.memo.section_level, self.state.memo.reporter
  121. with switch_source_input(self.state, content):
  122. self.state.nested_parse(content, 0, node, match_titles=1)
  123. return node.children