162306a36Sopenharmony_ci# -*- coding: utf-8; mode: python -*-
262306a36Sopenharmony_ci# coding=utf-8
362306a36Sopenharmony_ci# SPDX-License-Identifier: GPL-2.0
462306a36Sopenharmony_ci#
562306a36Sopenharmony_ciu"""
662306a36Sopenharmony_ci    kernel-abi
762306a36Sopenharmony_ci    ~~~~~~~~~~
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci    Implementation of the ``kernel-abi`` reST-directive.
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci    :copyright:  Copyright (C) 2016  Markus Heiser
1262306a36Sopenharmony_ci    :copyright:  Copyright (C) 2016-2020  Mauro Carvalho Chehab
1362306a36Sopenharmony_ci    :maintained-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
1462306a36Sopenharmony_ci    :license:    GPL Version 2, June 1991 see Linux/COPYING for details.
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci    The ``kernel-abi`` (:py:class:`KernelCmd`) directive calls the
1762306a36Sopenharmony_ci    scripts/get_abi.pl script to parse the Kernel ABI files.
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci    Overview of directive's argument and options.
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci    .. code-block:: rst
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci        .. kernel-abi:: <ABI directory location>
2462306a36Sopenharmony_ci            :debug:
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci    The argument ``<ABI directory location>`` is required. It contains the
2762306a36Sopenharmony_ci    location of the ABI files to be parsed.
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci    ``debug``
3062306a36Sopenharmony_ci      Inserts a code-block with the *raw* reST. Sometimes it is helpful to see
3162306a36Sopenharmony_ci      what reST is generated.
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci"""
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ciimport codecs
3662306a36Sopenharmony_ciimport os
3762306a36Sopenharmony_ciimport subprocess
3862306a36Sopenharmony_ciimport sys
3962306a36Sopenharmony_ciimport re
4062306a36Sopenharmony_ciimport kernellog
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_cifrom docutils import nodes, statemachine
4362306a36Sopenharmony_cifrom docutils.statemachine import ViewList
4462306a36Sopenharmony_cifrom docutils.parsers.rst import directives, Directive
4562306a36Sopenharmony_cifrom docutils.utils.error_reporting import ErrorString
4662306a36Sopenharmony_cifrom sphinx.util.docutils import switch_source_input
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci__version__  = '1.0'
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_cidef setup(app):
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci    app.add_directive("kernel-abi", KernelCmd)
5362306a36Sopenharmony_ci    return dict(
5462306a36Sopenharmony_ci        version = __version__
5562306a36Sopenharmony_ci        , parallel_read_safe = True
5662306a36Sopenharmony_ci        , parallel_write_safe = True
5762306a36Sopenharmony_ci    )
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ciclass KernelCmd(Directive):
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci    u"""KernelABI (``kernel-abi``) directive"""
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci    required_arguments = 1
6462306a36Sopenharmony_ci    optional_arguments = 2
6562306a36Sopenharmony_ci    has_content = False
6662306a36Sopenharmony_ci    final_argument_whitespace = True
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci    option_spec = {
6962306a36Sopenharmony_ci        "debug"     : directives.flag,
7062306a36Sopenharmony_ci        "rst"       : directives.unchanged
7162306a36Sopenharmony_ci    }
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci    def run(self):
7462306a36Sopenharmony_ci        doc = self.state.document
7562306a36Sopenharmony_ci        if not doc.settings.file_insertion_enabled:
7662306a36Sopenharmony_ci            raise self.warning("docutils: file insertion disabled")
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci        srctree = os.path.abspath(os.environ["srctree"])
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci        args = [
8162306a36Sopenharmony_ci            os.path.join(srctree, 'scripts/get_abi.pl'),
8262306a36Sopenharmony_ci            'rest',
8362306a36Sopenharmony_ci            '--enable-lineno',
8462306a36Sopenharmony_ci            '--dir', os.path.join(srctree, 'Documentation', self.arguments[0]),
8562306a36Sopenharmony_ci        ]
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci        if 'rst' in self.options:
8862306a36Sopenharmony_ci            args.append('--rst-source')
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci        lines = subprocess.check_output(args, cwd=os.path.dirname(doc.current_source)).decode('utf-8')
9162306a36Sopenharmony_ci        nodeList = self.nestedParse(lines, self.arguments[0])
9262306a36Sopenharmony_ci        return nodeList
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci    def nestedParse(self, lines, fname):
9562306a36Sopenharmony_ci        env = self.state.document.settings.env
9662306a36Sopenharmony_ci        content = ViewList()
9762306a36Sopenharmony_ci        node = nodes.section()
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci        if "debug" in self.options:
10062306a36Sopenharmony_ci            code_block = "\n\n.. code-block:: rst\n    :linenos:\n"
10162306a36Sopenharmony_ci            for l in lines.split("\n"):
10262306a36Sopenharmony_ci                code_block += "\n    " + l
10362306a36Sopenharmony_ci            lines = code_block + "\n\n"
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci        line_regex = re.compile(r"^\.\. LINENO (\S+)\#([0-9]+)$")
10662306a36Sopenharmony_ci        ln = 0
10762306a36Sopenharmony_ci        n = 0
10862306a36Sopenharmony_ci        f = fname
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci        for line in lines.split("\n"):
11162306a36Sopenharmony_ci            n = n + 1
11262306a36Sopenharmony_ci            match = line_regex.search(line)
11362306a36Sopenharmony_ci            if match:
11462306a36Sopenharmony_ci                new_f = match.group(1)
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci                # Sphinx parser is lazy: it stops parsing contents in the
11762306a36Sopenharmony_ci                # middle, if it is too big. So, handle it per input file
11862306a36Sopenharmony_ci                if new_f != f and content:
11962306a36Sopenharmony_ci                    self.do_parse(content, node)
12062306a36Sopenharmony_ci                    content = ViewList()
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci                    # Add the file to Sphinx build dependencies
12362306a36Sopenharmony_ci                    env.note_dependency(os.path.abspath(f))
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci                f = new_f
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci                # sphinx counts lines from 0
12862306a36Sopenharmony_ci                ln = int(match.group(2)) - 1
12962306a36Sopenharmony_ci            else:
13062306a36Sopenharmony_ci                content.append(line, f, ln)
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci        kernellog.info(self.state.document.settings.env.app, "%s: parsed %i lines" % (fname, n))
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci        if content:
13562306a36Sopenharmony_ci            self.do_parse(content, node)
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci        return node.children
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci    def do_parse(self, content, node):
14062306a36Sopenharmony_ci        with switch_source_input(self.state, content):
14162306a36Sopenharmony_ci            self.state.nested_parse(content, 0, node, match_titles=1)
142