kernel_abi.py 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. # -*- coding: utf-8; mode: python -*-
  2. # coding=utf-8
  3. # SPDX-License-Identifier: GPL-2.0
  4. #
  5. """
  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. AbiParser class 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 os
  26. import re
  27. import sys
  28. from docutils import nodes, statemachine
  29. from docutils.statemachine import ViewList
  30. from docutils.parsers.rst import directives, Directive
  31. from sphinx.util.docutils import switch_source_input
  32. from sphinx.util import logging
  33. srctree = os.path.abspath(os.environ["srctree"])
  34. sys.path.insert(0, os.path.join(srctree, "tools/lib/python"))
  35. from abi.abi_parser import AbiParser
  36. __version__ = "1.0"
  37. logger = logging.getLogger('kernel_abi')
  38. path = os.path.join(srctree, "Documentation/ABI")
  39. _kernel_abi = None
  40. def get_kernel_abi():
  41. """
  42. Initialize kernel_abi global var, if not initialized yet.
  43. This is needed to avoid warnings during Sphinx module initialization.
  44. """
  45. global _kernel_abi
  46. if not _kernel_abi:
  47. # Parse ABI symbols only once
  48. _kernel_abi = AbiParser(path, logger=logger)
  49. _kernel_abi.parse_abi()
  50. _kernel_abi.check_issues()
  51. return _kernel_abi
  52. def setup(app):
  53. app.add_directive("kernel-abi", KernelCmd)
  54. return {
  55. "version": __version__,
  56. "parallel_read_safe": True,
  57. "parallel_write_safe": True
  58. }
  59. class KernelCmd(Directive):
  60. """KernelABI (``kernel-abi``) directive"""
  61. required_arguments = 1
  62. optional_arguments = 3
  63. has_content = False
  64. final_argument_whitespace = True
  65. parser = None
  66. option_spec = {
  67. "debug": directives.flag,
  68. "no-symbols": directives.flag,
  69. "no-files": directives.flag,
  70. }
  71. def run(self):
  72. kernel_abi = get_kernel_abi()
  73. doc = self.state.document
  74. if not doc.settings.file_insertion_enabled:
  75. raise self.warning("docutils: file insertion disabled")
  76. env = self.state.document.settings.env
  77. content = ViewList()
  78. node = nodes.section()
  79. abi_type = self.arguments[0]
  80. if "no-symbols" in self.options:
  81. show_symbols = False
  82. else:
  83. show_symbols = True
  84. if "no-files" in self.options:
  85. show_file = False
  86. else:
  87. show_file = True
  88. tab_width = self.options.get('tab-width',
  89. self.state.document.settings.tab_width)
  90. old_f = None
  91. n = 0
  92. n_sym = 0
  93. for msg, f, ln in kernel_abi.doc(show_file=show_file,
  94. show_symbols=show_symbols,
  95. filter_path=abi_type):
  96. n_sym += 1
  97. msg_list = statemachine.string2lines(msg, tab_width,
  98. convert_whitespace=True)
  99. if "debug" in self.options:
  100. lines = [
  101. "", "", ".. code-block:: rst",
  102. " :linenos:", ""
  103. ]
  104. for m in msg_list:
  105. lines.append(" " + m)
  106. else:
  107. lines = msg_list
  108. for line in lines:
  109. # sphinx counts lines from 0
  110. content.append(line, f, ln - 1)
  111. n += 1
  112. if f != old_f:
  113. # Add the file to Sphinx build dependencies if the file exists
  114. fname = os.path.join(srctree, f)
  115. if os.path.isfile(fname):
  116. env.note_dependency(fname)
  117. old_f = f
  118. # Sphinx doesn't like to parse big messages. So, let's
  119. # add content symbol by symbol
  120. if content:
  121. self.do_parse(content, node)
  122. content = ViewList()
  123. if show_symbols and not show_file:
  124. logger.verbose("%s ABI: %i symbols (%i ReST lines)" % (abi_type, n_sym, n))
  125. elif not show_symbols and show_file:
  126. logger.verbose("%s ABI: %i files (%i ReST lines)" % (abi_type, n_sym, n))
  127. else:
  128. logger.verbose("%s ABI: %i data (%i ReST lines)" % (abi_type, n_sym, n))
  129. return node.children
  130. def do_parse(self, content, node):
  131. with switch_source_input(self.state, content):
  132. self.state.nested_parse(content, 0, node, match_titles=1)