kernel_abi.py 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. # -*- coding: utf-8; mode: python -*-
  2. # coding=utf-8
  3. # SPDX-License-Identifier: GPL-2.0
  4. #
  5. u"""
  6. kernel-abi
  7. ~~~~~~~~~~
  8. Implementation of the ``kernel-abi`` reST-directive.
  9. :copyright: Copyright (C) 2016 Markus Heiser
  10. :copyright: Copyright (C) 2016-2020 Mauro Carvalho Chehab
  11. :maintained-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
  12. :license: GPL Version 2, June 1991 see Linux/COPYING for details.
  13. The ``kernel-abi`` (:py:class:`KernelCmd`) directive calls the
  14. scripts/get_abi.pl script to parse the Kernel ABI files.
  15. Overview of directive's argument and options.
  16. .. code-block:: rst
  17. .. kernel-abi:: <ABI directory location>
  18. :debug:
  19. The argument ``<ABI directory location>`` is required. It contains the
  20. location of the ABI files to be parsed.
  21. ``debug``
  22. Inserts a code-block with the *raw* reST. Sometimes it is helpful to see
  23. what reST is generated.
  24. """
  25. import codecs
  26. import os
  27. import subprocess
  28. import sys
  29. import re
  30. import kernellog
  31. from docutils import nodes, statemachine
  32. from docutils.statemachine import ViewList
  33. from docutils.parsers.rst import directives, Directive
  34. from docutils.utils.error_reporting import ErrorString
  35. from sphinx.util.docutils import switch_source_input
  36. __version__ = '1.0'
  37. def setup(app):
  38. app.add_directive("kernel-abi", KernelCmd)
  39. return dict(
  40. version = __version__
  41. , parallel_read_safe = True
  42. , parallel_write_safe = True
  43. )
  44. class KernelCmd(Directive):
  45. u"""KernelABI (``kernel-abi``) directive"""
  46. required_arguments = 1
  47. optional_arguments = 2
  48. has_content = False
  49. final_argument_whitespace = True
  50. option_spec = {
  51. "debug" : directives.flag,
  52. "rst" : directives.unchanged
  53. }
  54. def run(self):
  55. doc = self.state.document
  56. if not doc.settings.file_insertion_enabled:
  57. raise self.warning("docutils: file insertion disabled")
  58. srctree = os.path.abspath(os.environ["srctree"])
  59. args = [
  60. os.path.join(srctree, 'scripts/get_abi.pl'),
  61. 'rest',
  62. '--enable-lineno',
  63. '--dir', os.path.join(srctree, 'Documentation', self.arguments[0]),
  64. ]
  65. if 'rst' in self.options:
  66. args.append('--rst-source')
  67. lines = subprocess.check_output(args, cwd=os.path.dirname(doc.current_source)).decode('utf-8')
  68. nodeList = self.nestedParse(lines, self.arguments[0])
  69. return nodeList
  70. def nestedParse(self, lines, fname):
  71. env = self.state.document.settings.env
  72. content = ViewList()
  73. node = nodes.section()
  74. if "debug" in self.options:
  75. code_block = "\n\n.. code-block:: rst\n :linenos:\n"
  76. for l in lines.split("\n"):
  77. code_block += "\n " + l
  78. lines = code_block + "\n\n"
  79. line_regex = re.compile(r"^\.\. LINENO (\S+)\#([0-9]+)$")
  80. ln = 0
  81. n = 0
  82. f = fname
  83. for line in lines.split("\n"):
  84. n = n + 1
  85. match = line_regex.search(line)
  86. if match:
  87. new_f = match.group(1)
  88. # Sphinx parser is lazy: it stops parsing contents in the
  89. # middle, if it is too big. So, handle it per input file
  90. if new_f != f and content:
  91. self.do_parse(content, node)
  92. content = ViewList()
  93. # Add the file to Sphinx build dependencies
  94. env.note_dependency(os.path.abspath(f))
  95. f = new_f
  96. # sphinx counts lines from 0
  97. ln = int(match.group(2)) - 1
  98. else:
  99. content.append(line, f, ln)
  100. kernellog.info(self.state.document.settings.env.app, "%s: parsed %i lines" % (fname, n))
  101. if content:
  102. self.do_parse(content, node)
  103. return node.children
  104. def do_parse(self, content, node):
  105. with switch_source_input(self.state, content):
  106. self.state.nested_parse(content, 0, node, match_titles=1)