18c2ecf20Sopenharmony_ci# -*- coding: utf-8; mode: python -*-
28c2ecf20Sopenharmony_ci# pylint: disable=C0103, R0903, R0912, R0915
38c2ecf20Sopenharmony_ciu"""
48c2ecf20Sopenharmony_ci    scalable figure and image handling
58c2ecf20Sopenharmony_ci    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci    Sphinx extension which implements scalable image handling.
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci    :copyright:  Copyright (C) 2016  Markus Heiser
108c2ecf20Sopenharmony_ci    :license:    GPL Version 2, June 1991 see Linux/COPYING for details.
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci    The build for image formats depend on image's source format and output's
138c2ecf20Sopenharmony_ci    destination format. This extension implement methods to simplify image
148c2ecf20Sopenharmony_ci    handling from the author's POV. Directives like ``kernel-figure`` implement
158c2ecf20Sopenharmony_ci    methods *to* always get the best output-format even if some tools are not
168c2ecf20Sopenharmony_ci    installed. For more details take a look at ``convert_image(...)`` which is
178c2ecf20Sopenharmony_ci    the core of all conversions.
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci    * ``.. kernel-image``: for image handling / a ``.. image::`` replacement
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci    * ``.. kernel-figure``: for figure handling / a ``.. figure::`` replacement
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci    * ``.. kernel-render``: for render markup / a concept to embed *render*
248c2ecf20Sopenharmony_ci      markups (or languages). Supported markups (see ``RENDER_MARKUP_EXT``)
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci      - ``DOT``: render embedded Graphviz's **DOC**
278c2ecf20Sopenharmony_ci      - ``SVG``: render embedded Scalable Vector Graphics (**SVG**)
288c2ecf20Sopenharmony_ci      - ... *developable*
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci    Used tools:
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci    * ``dot(1)``: Graphviz (https://www.graphviz.org). If Graphviz is not
338c2ecf20Sopenharmony_ci      available, the DOT language is inserted as literal-block.
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci    * SVG to PDF: To generate PDF, you need at least one of this tools:
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci      - ``convert(1)``: ImageMagick (https://www.imagemagick.org)
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci    List of customizations:
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci    * generate PDF from SVG / used by PDF (LaTeX) builder
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci    * generate SVG (html-builder) and PDF (latex-builder) from DOT files.
448c2ecf20Sopenharmony_ci      DOT: see https://www.graphviz.org/content/dot-language
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci    """
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ciimport os
498c2ecf20Sopenharmony_cifrom os import path
508c2ecf20Sopenharmony_ciimport subprocess
518c2ecf20Sopenharmony_cifrom hashlib import sha1
528c2ecf20Sopenharmony_ciimport sys
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_cifrom docutils import nodes
558c2ecf20Sopenharmony_cifrom docutils.statemachine import ViewList
568c2ecf20Sopenharmony_cifrom docutils.parsers.rst import directives
578c2ecf20Sopenharmony_cifrom docutils.parsers.rst.directives import images
588c2ecf20Sopenharmony_ciimport sphinx
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_cifrom sphinx.util.nodes import clean_astext
618c2ecf20Sopenharmony_cifrom six import iteritems
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ciimport kernellog
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ciPY3 = sys.version_info[0] == 3
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ciif PY3:
688c2ecf20Sopenharmony_ci    _unicode = str
698c2ecf20Sopenharmony_cielse:
708c2ecf20Sopenharmony_ci    _unicode = unicode
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci# Get Sphinx version
738c2ecf20Sopenharmony_cimajor, minor, patch = sphinx.version_info[:3]
748c2ecf20Sopenharmony_ciif major == 1 and minor > 3:
758c2ecf20Sopenharmony_ci    # patches.Figure only landed in Sphinx 1.4
768c2ecf20Sopenharmony_ci    from sphinx.directives.patches import Figure  # pylint: disable=C0413
778c2ecf20Sopenharmony_cielse:
788c2ecf20Sopenharmony_ci    Figure = images.Figure
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci__version__  = '1.0.0'
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci# simple helper
838c2ecf20Sopenharmony_ci# -------------
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_cidef which(cmd):
868c2ecf20Sopenharmony_ci    """Searches the ``cmd`` in the ``PATH`` environment.
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci    This *which* searches the PATH for executable ``cmd`` . First match is
898c2ecf20Sopenharmony_ci    returned, if nothing is found, ``None` is returned.
908c2ecf20Sopenharmony_ci    """
918c2ecf20Sopenharmony_ci    envpath = os.environ.get('PATH', None) or os.defpath
928c2ecf20Sopenharmony_ci    for folder in envpath.split(os.pathsep):
938c2ecf20Sopenharmony_ci        fname = folder + os.sep + cmd
948c2ecf20Sopenharmony_ci        if path.isfile(fname):
958c2ecf20Sopenharmony_ci            return fname
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_cidef mkdir(folder, mode=0o775):
988c2ecf20Sopenharmony_ci    if not path.isdir(folder):
998c2ecf20Sopenharmony_ci        os.makedirs(folder, mode)
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_cidef file2literal(fname):
1028c2ecf20Sopenharmony_ci    with open(fname, "r") as src:
1038c2ecf20Sopenharmony_ci        data = src.read()
1048c2ecf20Sopenharmony_ci        node = nodes.literal_block(data, data)
1058c2ecf20Sopenharmony_ci    return node
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_cidef isNewer(path1, path2):
1088c2ecf20Sopenharmony_ci    """Returns True if ``path1`` is newer than ``path2``
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci    If ``path1`` exists and is newer than ``path2`` the function returns
1118c2ecf20Sopenharmony_ci    ``True`` is returned otherwise ``False``
1128c2ecf20Sopenharmony_ci    """
1138c2ecf20Sopenharmony_ci    return (path.exists(path1)
1148c2ecf20Sopenharmony_ci            and os.stat(path1).st_ctime > os.stat(path2).st_ctime)
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_cidef pass_handle(self, node):           # pylint: disable=W0613
1178c2ecf20Sopenharmony_ci    pass
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci# setup conversion tools and sphinx extension
1208c2ecf20Sopenharmony_ci# -------------------------------------------
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci# Graphviz's dot(1) support
1238c2ecf20Sopenharmony_cidot_cmd = None
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci# ImageMagick' convert(1) support
1268c2ecf20Sopenharmony_ciconvert_cmd = None
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_cidef setup(app):
1308c2ecf20Sopenharmony_ci    # check toolchain first
1318c2ecf20Sopenharmony_ci    app.connect('builder-inited', setupTools)
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci    # image handling
1348c2ecf20Sopenharmony_ci    app.add_directive("kernel-image",  KernelImage)
1358c2ecf20Sopenharmony_ci    app.add_node(kernel_image,
1368c2ecf20Sopenharmony_ci                 html    = (visit_kernel_image, pass_handle),
1378c2ecf20Sopenharmony_ci                 latex   = (visit_kernel_image, pass_handle),
1388c2ecf20Sopenharmony_ci                 texinfo = (visit_kernel_image, pass_handle),
1398c2ecf20Sopenharmony_ci                 text    = (visit_kernel_image, pass_handle),
1408c2ecf20Sopenharmony_ci                 man     = (visit_kernel_image, pass_handle), )
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci    # figure handling
1438c2ecf20Sopenharmony_ci    app.add_directive("kernel-figure", KernelFigure)
1448c2ecf20Sopenharmony_ci    app.add_node(kernel_figure,
1458c2ecf20Sopenharmony_ci                 html    = (visit_kernel_figure, pass_handle),
1468c2ecf20Sopenharmony_ci                 latex   = (visit_kernel_figure, pass_handle),
1478c2ecf20Sopenharmony_ci                 texinfo = (visit_kernel_figure, pass_handle),
1488c2ecf20Sopenharmony_ci                 text    = (visit_kernel_figure, pass_handle),
1498c2ecf20Sopenharmony_ci                 man     = (visit_kernel_figure, pass_handle), )
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci    # render handling
1528c2ecf20Sopenharmony_ci    app.add_directive('kernel-render', KernelRender)
1538c2ecf20Sopenharmony_ci    app.add_node(kernel_render,
1548c2ecf20Sopenharmony_ci                 html    = (visit_kernel_render, pass_handle),
1558c2ecf20Sopenharmony_ci                 latex   = (visit_kernel_render, pass_handle),
1568c2ecf20Sopenharmony_ci                 texinfo = (visit_kernel_render, pass_handle),
1578c2ecf20Sopenharmony_ci                 text    = (visit_kernel_render, pass_handle),
1588c2ecf20Sopenharmony_ci                 man     = (visit_kernel_render, pass_handle), )
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci    app.connect('doctree-read', add_kernel_figure_to_std_domain)
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci    return dict(
1638c2ecf20Sopenharmony_ci        version = __version__,
1648c2ecf20Sopenharmony_ci        parallel_read_safe = True,
1658c2ecf20Sopenharmony_ci        parallel_write_safe = True
1668c2ecf20Sopenharmony_ci    )
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_cidef setupTools(app):
1708c2ecf20Sopenharmony_ci    u"""
1718c2ecf20Sopenharmony_ci    Check available build tools and log some *verbose* messages.
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci    This function is called once, when the builder is initiated.
1748c2ecf20Sopenharmony_ci    """
1758c2ecf20Sopenharmony_ci    global dot_cmd, convert_cmd   # pylint: disable=W0603
1768c2ecf20Sopenharmony_ci    kernellog.verbose(app, "kfigure: check installed tools ...")
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci    dot_cmd = which('dot')
1798c2ecf20Sopenharmony_ci    convert_cmd = which('convert')
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci    if dot_cmd:
1828c2ecf20Sopenharmony_ci        kernellog.verbose(app, "use dot(1) from: " + dot_cmd)
1838c2ecf20Sopenharmony_ci    else:
1848c2ecf20Sopenharmony_ci        kernellog.warn(app, "dot(1) not found, for better output quality install "
1858c2ecf20Sopenharmony_ci                       "graphviz from https://www.graphviz.org")
1868c2ecf20Sopenharmony_ci    if convert_cmd:
1878c2ecf20Sopenharmony_ci        kernellog.verbose(app, "use convert(1) from: " + convert_cmd)
1888c2ecf20Sopenharmony_ci    else:
1898c2ecf20Sopenharmony_ci        kernellog.warn(app,
1908c2ecf20Sopenharmony_ci            "convert(1) not found, for SVG to PDF conversion install "
1918c2ecf20Sopenharmony_ci            "ImageMagick (https://www.imagemagick.org)")
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci# integrate conversion tools
1958c2ecf20Sopenharmony_ci# --------------------------
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ciRENDER_MARKUP_EXT = {
1988c2ecf20Sopenharmony_ci    # The '.ext' must be handled by convert_image(..) function's *in_ext* input.
1998c2ecf20Sopenharmony_ci    # <name> : <.ext>
2008c2ecf20Sopenharmony_ci    'DOT' : '.dot',
2018c2ecf20Sopenharmony_ci    'SVG' : '.svg'
2028c2ecf20Sopenharmony_ci}
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_cidef convert_image(img_node, translator, src_fname=None):
2058c2ecf20Sopenharmony_ci    """Convert a image node for the builder.
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci    Different builder prefer different image formats, e.g. *latex* builder
2088c2ecf20Sopenharmony_ci    prefer PDF while *html* builder prefer SVG format for images.
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci    This function handles output image formats in dependence of source the
2118c2ecf20Sopenharmony_ci    format (of the image) and the translator's output format.
2128c2ecf20Sopenharmony_ci    """
2138c2ecf20Sopenharmony_ci    app = translator.builder.app
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci    fname, in_ext = path.splitext(path.basename(img_node['uri']))
2168c2ecf20Sopenharmony_ci    if src_fname is None:
2178c2ecf20Sopenharmony_ci        src_fname = path.join(translator.builder.srcdir, img_node['uri'])
2188c2ecf20Sopenharmony_ci        if not path.exists(src_fname):
2198c2ecf20Sopenharmony_ci            src_fname = path.join(translator.builder.outdir, img_node['uri'])
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci    dst_fname = None
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci    # in kernel builds, use 'make SPHINXOPTS=-v' to see verbose messages
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci    kernellog.verbose(app, 'assert best format for: ' + img_node['uri'])
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci    if in_ext == '.dot':
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci        if not dot_cmd:
2308c2ecf20Sopenharmony_ci            kernellog.verbose(app,
2318c2ecf20Sopenharmony_ci                              "dot from graphviz not available / include DOT raw.")
2328c2ecf20Sopenharmony_ci            img_node.replace_self(file2literal(src_fname))
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci        elif translator.builder.format == 'latex':
2358c2ecf20Sopenharmony_ci            dst_fname = path.join(translator.builder.outdir, fname + '.pdf')
2368c2ecf20Sopenharmony_ci            img_node['uri'] = fname + '.pdf'
2378c2ecf20Sopenharmony_ci            img_node['candidates'] = {'*': fname + '.pdf'}
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci        elif translator.builder.format == 'html':
2418c2ecf20Sopenharmony_ci            dst_fname = path.join(
2428c2ecf20Sopenharmony_ci                translator.builder.outdir,
2438c2ecf20Sopenharmony_ci                translator.builder.imagedir,
2448c2ecf20Sopenharmony_ci                fname + '.svg')
2458c2ecf20Sopenharmony_ci            img_node['uri'] = path.join(
2468c2ecf20Sopenharmony_ci                translator.builder.imgpath, fname + '.svg')
2478c2ecf20Sopenharmony_ci            img_node['candidates'] = {
2488c2ecf20Sopenharmony_ci                '*': path.join(translator.builder.imgpath, fname + '.svg')}
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci        else:
2518c2ecf20Sopenharmony_ci            # all other builder formats will include DOT as raw
2528c2ecf20Sopenharmony_ci            img_node.replace_self(file2literal(src_fname))
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci    elif in_ext == '.svg':
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci        if translator.builder.format == 'latex':
2578c2ecf20Sopenharmony_ci            if convert_cmd is None:
2588c2ecf20Sopenharmony_ci                kernellog.verbose(app,
2598c2ecf20Sopenharmony_ci                                  "no SVG to PDF conversion available / include SVG raw.")
2608c2ecf20Sopenharmony_ci                img_node.replace_self(file2literal(src_fname))
2618c2ecf20Sopenharmony_ci            else:
2628c2ecf20Sopenharmony_ci                dst_fname = path.join(translator.builder.outdir, fname + '.pdf')
2638c2ecf20Sopenharmony_ci                img_node['uri'] = fname + '.pdf'
2648c2ecf20Sopenharmony_ci                img_node['candidates'] = {'*': fname + '.pdf'}
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci    if dst_fname:
2678c2ecf20Sopenharmony_ci        # the builder needs not to copy one more time, so pop it if exists.
2688c2ecf20Sopenharmony_ci        translator.builder.images.pop(img_node['uri'], None)
2698c2ecf20Sopenharmony_ci        _name = dst_fname[len(translator.builder.outdir) + 1:]
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci        if isNewer(dst_fname, src_fname):
2728c2ecf20Sopenharmony_ci            kernellog.verbose(app,
2738c2ecf20Sopenharmony_ci                              "convert: {out}/%s already exists and is newer" % _name)
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci        else:
2768c2ecf20Sopenharmony_ci            ok = False
2778c2ecf20Sopenharmony_ci            mkdir(path.dirname(dst_fname))
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci            if in_ext == '.dot':
2808c2ecf20Sopenharmony_ci                kernellog.verbose(app, 'convert DOT to: {out}/' + _name)
2818c2ecf20Sopenharmony_ci                ok = dot2format(app, src_fname, dst_fname)
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci            elif in_ext == '.svg':
2848c2ecf20Sopenharmony_ci                kernellog.verbose(app, 'convert SVG to: {out}/' + _name)
2858c2ecf20Sopenharmony_ci                ok = svg2pdf(app, src_fname, dst_fname)
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci            if not ok:
2888c2ecf20Sopenharmony_ci                img_node.replace_self(file2literal(src_fname))
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_cidef dot2format(app, dot_fname, out_fname):
2928c2ecf20Sopenharmony_ci    """Converts DOT file to ``out_fname`` using ``dot(1)``.
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci    * ``dot_fname`` pathname of the input DOT file, including extension ``.dot``
2958c2ecf20Sopenharmony_ci    * ``out_fname`` pathname of the output file, including format extension
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci    The *format extension* depends on the ``dot`` command (see ``man dot``
2988c2ecf20Sopenharmony_ci    option ``-Txxx``). Normally you will use one of the following extensions:
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci    - ``.ps`` for PostScript,
3018c2ecf20Sopenharmony_ci    - ``.svg`` or ``svgz`` for Structured Vector Graphics,
3028c2ecf20Sopenharmony_ci    - ``.fig`` for XFIG graphics and
3038c2ecf20Sopenharmony_ci    - ``.png`` or ``gif`` for common bitmap graphics.
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci    """
3068c2ecf20Sopenharmony_ci    out_format = path.splitext(out_fname)[1][1:]
3078c2ecf20Sopenharmony_ci    cmd = [dot_cmd, '-T%s' % out_format, dot_fname]
3088c2ecf20Sopenharmony_ci    exit_code = 42
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci    with open(out_fname, "w") as out:
3118c2ecf20Sopenharmony_ci        exit_code = subprocess.call(cmd, stdout = out)
3128c2ecf20Sopenharmony_ci        if exit_code != 0:
3138c2ecf20Sopenharmony_ci            kernellog.warn(app,
3148c2ecf20Sopenharmony_ci                          "Error #%d when calling: %s" % (exit_code, " ".join(cmd)))
3158c2ecf20Sopenharmony_ci    return bool(exit_code == 0)
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_cidef svg2pdf(app, svg_fname, pdf_fname):
3188c2ecf20Sopenharmony_ci    """Converts SVG to PDF with ``convert(1)`` command.
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci    Uses ``convert(1)`` from ImageMagick (https://www.imagemagick.org) for
3218c2ecf20Sopenharmony_ci    conversion.  Returns ``True`` on success and ``False`` if an error occurred.
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci    * ``svg_fname`` pathname of the input SVG file with extension (``.svg``)
3248c2ecf20Sopenharmony_ci    * ``pdf_name``  pathname of the output PDF file with extension (``.pdf``)
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci    """
3278c2ecf20Sopenharmony_ci    cmd = [convert_cmd, svg_fname, pdf_fname]
3288c2ecf20Sopenharmony_ci    # use stdout and stderr from parent
3298c2ecf20Sopenharmony_ci    exit_code = subprocess.call(cmd)
3308c2ecf20Sopenharmony_ci    if exit_code != 0:
3318c2ecf20Sopenharmony_ci        kernellog.warn(app, "Error #%d when calling: %s" % (exit_code, " ".join(cmd)))
3328c2ecf20Sopenharmony_ci    return bool(exit_code == 0)
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci# image handling
3368c2ecf20Sopenharmony_ci# ---------------------
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_cidef visit_kernel_image(self, node):    # pylint: disable=W0613
3398c2ecf20Sopenharmony_ci    """Visitor of the ``kernel_image`` Node.
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci    Handles the ``image`` child-node with the ``convert_image(...)``.
3428c2ecf20Sopenharmony_ci    """
3438c2ecf20Sopenharmony_ci    img_node = node[0]
3448c2ecf20Sopenharmony_ci    convert_image(img_node, self)
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_ciclass kernel_image(nodes.image):
3478c2ecf20Sopenharmony_ci    """Node for ``kernel-image`` directive."""
3488c2ecf20Sopenharmony_ci    pass
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ciclass KernelImage(images.Image):
3518c2ecf20Sopenharmony_ci    u"""KernelImage directive
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci    Earns everything from ``.. image::`` directive, except *remote URI* and
3548c2ecf20Sopenharmony_ci    *glob* pattern. The KernelImage wraps a image node into a
3558c2ecf20Sopenharmony_ci    kernel_image node. See ``visit_kernel_image``.
3568c2ecf20Sopenharmony_ci    """
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci    def run(self):
3598c2ecf20Sopenharmony_ci        uri = self.arguments[0]
3608c2ecf20Sopenharmony_ci        if uri.endswith('.*') or uri.find('://') != -1:
3618c2ecf20Sopenharmony_ci            raise self.severe(
3628c2ecf20Sopenharmony_ci                'Error in "%s: %s": glob pattern and remote images are not allowed'
3638c2ecf20Sopenharmony_ci                % (self.name, uri))
3648c2ecf20Sopenharmony_ci        result = images.Image.run(self)
3658c2ecf20Sopenharmony_ci        if len(result) == 2 or isinstance(result[0], nodes.system_message):
3668c2ecf20Sopenharmony_ci            return result
3678c2ecf20Sopenharmony_ci        (image_node,) = result
3688c2ecf20Sopenharmony_ci        # wrap image node into a kernel_image node / see visitors
3698c2ecf20Sopenharmony_ci        node = kernel_image('', image_node)
3708c2ecf20Sopenharmony_ci        return [node]
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci# figure handling
3738c2ecf20Sopenharmony_ci# ---------------------
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_cidef visit_kernel_figure(self, node):   # pylint: disable=W0613
3768c2ecf20Sopenharmony_ci    """Visitor of the ``kernel_figure`` Node.
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci    Handles the ``image`` child-node with the ``convert_image(...)``.
3798c2ecf20Sopenharmony_ci    """
3808c2ecf20Sopenharmony_ci    img_node = node[0][0]
3818c2ecf20Sopenharmony_ci    convert_image(img_node, self)
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ciclass kernel_figure(nodes.figure):
3848c2ecf20Sopenharmony_ci    """Node for ``kernel-figure`` directive."""
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ciclass KernelFigure(Figure):
3878c2ecf20Sopenharmony_ci    u"""KernelImage directive
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci    Earns everything from ``.. figure::`` directive, except *remote URI* and
3908c2ecf20Sopenharmony_ci    *glob* pattern.  The KernelFigure wraps a figure node into a kernel_figure
3918c2ecf20Sopenharmony_ci    node. See ``visit_kernel_figure``.
3928c2ecf20Sopenharmony_ci    """
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci    def run(self):
3958c2ecf20Sopenharmony_ci        uri = self.arguments[0]
3968c2ecf20Sopenharmony_ci        if uri.endswith('.*') or uri.find('://') != -1:
3978c2ecf20Sopenharmony_ci            raise self.severe(
3988c2ecf20Sopenharmony_ci                'Error in "%s: %s":'
3998c2ecf20Sopenharmony_ci                ' glob pattern and remote images are not allowed'
4008c2ecf20Sopenharmony_ci                % (self.name, uri))
4018c2ecf20Sopenharmony_ci        result = Figure.run(self)
4028c2ecf20Sopenharmony_ci        if len(result) == 2 or isinstance(result[0], nodes.system_message):
4038c2ecf20Sopenharmony_ci            return result
4048c2ecf20Sopenharmony_ci        (figure_node,) = result
4058c2ecf20Sopenharmony_ci        # wrap figure node into a kernel_figure node / see visitors
4068c2ecf20Sopenharmony_ci        node = kernel_figure('', figure_node)
4078c2ecf20Sopenharmony_ci        return [node]
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci# render handling
4118c2ecf20Sopenharmony_ci# ---------------------
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_cidef visit_kernel_render(self, node):
4148c2ecf20Sopenharmony_ci    """Visitor of the ``kernel_render`` Node.
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci    If rendering tools available, save the markup of the ``literal_block`` child
4178c2ecf20Sopenharmony_ci    node into a file and replace the ``literal_block`` node with a new created
4188c2ecf20Sopenharmony_ci    ``image`` node, pointing to the saved markup file. Afterwards, handle the
4198c2ecf20Sopenharmony_ci    image child-node with the ``convert_image(...)``.
4208c2ecf20Sopenharmony_ci    """
4218c2ecf20Sopenharmony_ci    app = self.builder.app
4228c2ecf20Sopenharmony_ci    srclang = node.get('srclang')
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ci    kernellog.verbose(app, 'visit kernel-render node lang: "%s"' % (srclang))
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_ci    tmp_ext = RENDER_MARKUP_EXT.get(srclang, None)
4278c2ecf20Sopenharmony_ci    if tmp_ext is None:
4288c2ecf20Sopenharmony_ci        kernellog.warn(app, 'kernel-render: "%s" unknown / include raw.' % (srclang))
4298c2ecf20Sopenharmony_ci        return
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci    if not dot_cmd and tmp_ext == '.dot':
4328c2ecf20Sopenharmony_ci        kernellog.verbose(app, "dot from graphviz not available / include raw.")
4338c2ecf20Sopenharmony_ci        return
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci    literal_block = node[0]
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_ci    code      = literal_block.astext()
4388c2ecf20Sopenharmony_ci    hashobj   = code.encode('utf-8') #  str(node.attributes)
4398c2ecf20Sopenharmony_ci    fname     = path.join('%s-%s' % (srclang, sha1(hashobj).hexdigest()))
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci    tmp_fname = path.join(
4428c2ecf20Sopenharmony_ci        self.builder.outdir, self.builder.imagedir, fname + tmp_ext)
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci    if not path.isfile(tmp_fname):
4458c2ecf20Sopenharmony_ci        mkdir(path.dirname(tmp_fname))
4468c2ecf20Sopenharmony_ci        with open(tmp_fname, "w") as out:
4478c2ecf20Sopenharmony_ci            out.write(code)
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_ci    img_node = nodes.image(node.rawsource, **node.attributes)
4508c2ecf20Sopenharmony_ci    img_node['uri'] = path.join(self.builder.imgpath, fname + tmp_ext)
4518c2ecf20Sopenharmony_ci    img_node['candidates'] = {
4528c2ecf20Sopenharmony_ci        '*': path.join(self.builder.imgpath, fname + tmp_ext)}
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_ci    literal_block.replace_self(img_node)
4558c2ecf20Sopenharmony_ci    convert_image(img_node, self, tmp_fname)
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_ciclass kernel_render(nodes.General, nodes.Inline, nodes.Element):
4598c2ecf20Sopenharmony_ci    """Node for ``kernel-render`` directive."""
4608c2ecf20Sopenharmony_ci    pass
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_ciclass KernelRender(Figure):
4638c2ecf20Sopenharmony_ci    u"""KernelRender directive
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_ci    Render content by external tool.  Has all the options known from the
4668c2ecf20Sopenharmony_ci    *figure*  directive, plus option ``caption``.  If ``caption`` has a
4678c2ecf20Sopenharmony_ci    value, a figure node with the *caption* is inserted. If not, a image node is
4688c2ecf20Sopenharmony_ci    inserted.
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci    The KernelRender directive wraps the text of the directive into a
4718c2ecf20Sopenharmony_ci    literal_block node and wraps it into a kernel_render node. See
4728c2ecf20Sopenharmony_ci    ``visit_kernel_render``.
4738c2ecf20Sopenharmony_ci    """
4748c2ecf20Sopenharmony_ci    has_content = True
4758c2ecf20Sopenharmony_ci    required_arguments = 1
4768c2ecf20Sopenharmony_ci    optional_arguments = 0
4778c2ecf20Sopenharmony_ci    final_argument_whitespace = False
4788c2ecf20Sopenharmony_ci
4798c2ecf20Sopenharmony_ci    # earn options from 'figure'
4808c2ecf20Sopenharmony_ci    option_spec = Figure.option_spec.copy()
4818c2ecf20Sopenharmony_ci    option_spec['caption'] = directives.unchanged
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_ci    def run(self):
4848c2ecf20Sopenharmony_ci        return [self.build_node()]
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ci    def build_node(self):
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_ci        srclang = self.arguments[0].strip()
4898c2ecf20Sopenharmony_ci        if srclang not in RENDER_MARKUP_EXT.keys():
4908c2ecf20Sopenharmony_ci            return [self.state_machine.reporter.warning(
4918c2ecf20Sopenharmony_ci                'Unknown source language "%s", use one of: %s.' % (
4928c2ecf20Sopenharmony_ci                    srclang, ",".join(RENDER_MARKUP_EXT.keys())),
4938c2ecf20Sopenharmony_ci                line=self.lineno)]
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_ci        code = '\n'.join(self.content)
4968c2ecf20Sopenharmony_ci        if not code.strip():
4978c2ecf20Sopenharmony_ci            return [self.state_machine.reporter.warning(
4988c2ecf20Sopenharmony_ci                'Ignoring "%s" directive without content.' % (
4998c2ecf20Sopenharmony_ci                    self.name),
5008c2ecf20Sopenharmony_ci                line=self.lineno)]
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_ci        node = kernel_render()
5038c2ecf20Sopenharmony_ci        node['alt'] = self.options.get('alt','')
5048c2ecf20Sopenharmony_ci        node['srclang'] = srclang
5058c2ecf20Sopenharmony_ci        literal_node = nodes.literal_block(code, code)
5068c2ecf20Sopenharmony_ci        node += literal_node
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_ci        caption = self.options.get('caption')
5098c2ecf20Sopenharmony_ci        if caption:
5108c2ecf20Sopenharmony_ci            # parse caption's content
5118c2ecf20Sopenharmony_ci            parsed = nodes.Element()
5128c2ecf20Sopenharmony_ci            self.state.nested_parse(
5138c2ecf20Sopenharmony_ci                ViewList([caption], source=''), self.content_offset, parsed)
5148c2ecf20Sopenharmony_ci            caption_node = nodes.caption(
5158c2ecf20Sopenharmony_ci                parsed[0].rawsource, '', *parsed[0].children)
5168c2ecf20Sopenharmony_ci            caption_node.source = parsed[0].source
5178c2ecf20Sopenharmony_ci            caption_node.line = parsed[0].line
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_ci            figure_node = nodes.figure('', node)
5208c2ecf20Sopenharmony_ci            for k,v in self.options.items():
5218c2ecf20Sopenharmony_ci                figure_node[k] = v
5228c2ecf20Sopenharmony_ci            figure_node += caption_node
5238c2ecf20Sopenharmony_ci
5248c2ecf20Sopenharmony_ci            node = figure_node
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_ci        return node
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_cidef add_kernel_figure_to_std_domain(app, doctree):
5298c2ecf20Sopenharmony_ci    """Add kernel-figure anchors to 'std' domain.
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_ci    The ``StandardDomain.process_doc(..)`` method does not know how to resolve
5328c2ecf20Sopenharmony_ci    the caption (label) of ``kernel-figure`` directive (it only knows about
5338c2ecf20Sopenharmony_ci    standard nodes, e.g. table, figure etc.). Without any additional handling
5348c2ecf20Sopenharmony_ci    this will result in a 'undefined label' for kernel-figures.
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_ci    This handle adds labels of kernel-figure to the 'std' domain labels.
5378c2ecf20Sopenharmony_ci    """
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_ci    std = app.env.domains["std"]
5408c2ecf20Sopenharmony_ci    docname = app.env.docname
5418c2ecf20Sopenharmony_ci    labels = std.data["labels"]
5428c2ecf20Sopenharmony_ci
5438c2ecf20Sopenharmony_ci    for name, explicit in iteritems(doctree.nametypes):
5448c2ecf20Sopenharmony_ci        if not explicit:
5458c2ecf20Sopenharmony_ci            continue
5468c2ecf20Sopenharmony_ci        labelid = doctree.nameids[name]
5478c2ecf20Sopenharmony_ci        if labelid is None:
5488c2ecf20Sopenharmony_ci            continue
5498c2ecf20Sopenharmony_ci        node = doctree.ids[labelid]
5508c2ecf20Sopenharmony_ci
5518c2ecf20Sopenharmony_ci        if node.tagname == 'kernel_figure':
5528c2ecf20Sopenharmony_ci            for n in node.next_node():
5538c2ecf20Sopenharmony_ci                if n.tagname == 'caption':
5548c2ecf20Sopenharmony_ci                    sectname = clean_astext(n)
5558c2ecf20Sopenharmony_ci                    # add label to std domain
5568c2ecf20Sopenharmony_ci                    labels[name] = docname, labelid, sectname
5578c2ecf20Sopenharmony_ci                    break
558