xref: /dpdk/doc/guides/conf.py (revision f4ccce58c1a33cb41e1e820da504698437987efc)
1#!/usr/bin/env python3
2# SPDX-License-Identifier: BSD-3-Clause
3# Copyright(c) 2010-2015 Intel Corporation
4
5from docutils import nodes
6from packaging.version import Version
7from sphinx import __version__ as sphinx_version
8from os import listdir
9from os import environ
10from os.path import basename
11from os.path import dirname
12from os.path import join as path_join
13from sys import argv, stderr, path
14
15import configparser
16
17try:
18    import sphinx_rtd_theme
19    html_theme = "sphinx_rtd_theme"
20except ImportError:
21    print('Install the sphinx ReadTheDocs theme for improved html documentation '
22          'layout: https://sphinx-rtd-theme.readthedocs.io/',
23          file=stderr)
24    html_theme = "default"
25
26stop_on_error = ('-W' in argv)
27
28project = 'Data Plane Development Kit'
29html_logo = '../logo/DPDK_logo_vertical_rev_small.png'
30if Version(sphinx_version) >= Version('3.5'):
31    html_permalinks = False
32else:
33    html_add_permalinks = ""
34html_show_copyright = False
35highlight_language = 'none'
36
37release = environ.setdefault('DPDK_VERSION', "None")
38version = release
39
40master_doc = 'index'
41
42# Maximum feature description string length
43feature_str_len = 30
44
45# Figures, tables and code-blocks automatically numbered if they have caption
46numfig = True
47
48# Configuration for man pages
49man_pages = [("testpmd_app_ug/run_app", "testpmd",
50              "tests for dpdk pmds", "", 1),
51             ("tools/pdump", "dpdk-pdump",
52              "enable packet capture on dpdk ports", "", 1),
53             ("tools/proc_info", "dpdk-procinfo",
54              "access dpdk port stats and memory info", "", 1),
55             ("tools/pmdinfo", "dpdk-pmdinfo",
56              "dump a PMDs hardware support info", "", 1),
57             ("tools/devbind", "dpdk-devbind",
58              "check device status and bind/unbind them from drivers", "", 8)]
59
60# DTS API docs additional configuration
61if environ.get('DTS_DOC_BUILD'):
62    extensions = ['sphinx.ext.napoleon', 'sphinx.ext.autodoc']
63
64    # Pydantic models require autodoc_pydantic for the right formatting
65    try:
66        import sphinxcontrib.autodoc_pydantic
67        extensions.append("sphinxcontrib.autodoc_pydantic")
68    except ImportError:
69        print(
70            "The DTS API doc dependencies are missing. "
71            "The generated output won't be as intended, "
72            "and autodoc may throw unexpected warnings.",
73            file=stderr,
74        )
75
76    # Napoleon enables the Google format of Python doscstrings.
77    napoleon_numpy_docstring = False
78    napoleon_attr_annotations = True
79    napoleon_preprocess_types = True
80
81    # Autodoc pulls documentation from code.
82    autodoc_default_options = {
83        'members': True,
84        'member-order': 'bysource',
85        'show-inheritance': True,
86    }
87    autodoc_class_signature = 'separated'
88    autodoc_typehints = 'both'
89    autodoc_typehints_format = 'short'
90    autodoc_typehints_description_target = 'documented'
91
92    # DTS docstring options.
93    add_module_names = False
94    toc_object_entries = True
95    toc_object_entries_show_parents = 'hide'
96    # DTS Sidebar config.
97    if html_theme == "sphinx_rtd_theme":
98        html_theme_options = {
99            'collapse_navigation': False,
100            'navigation_depth': -1,  # unlimited depth
101        }
102
103    # Add path to DTS sources so that Sphinx can find them.
104    dpdk_root = dirname(dirname(dirname(__file__)))
105    path.append(path_join(dpdk_root, 'dts'))
106
107    # Get missing DTS dependencies. Add path to buildtools to find the get_missing_imports function.
108    path.append(path_join(dpdk_root, 'buildtools'))
109    import importlib
110    # Ignore missing imports from DTS dependencies, allowing us to build the docs without them.
111    # There's almost no difference between docs built with and without dependencies.
112    # The qualified names of imported objects are fully expanded with dependencies, such as:
113    # fabric.Connection (without) vs. fabric.connection.Connection (with)
114    autodoc_mock_imports = importlib.import_module('check-dts-requirements').get_missing_imports()
115
116
117# ####### :numref: fallback ########
118# The following hook functions add some simple handling for the :numref:
119# directive for Sphinx versions prior to 1.3.1. The functions replace the
120# :numref: reference with a link to the target (for all Sphinx doc types).
121# It doesn't try to label figures/tables.
122def numref_role(reftype, rawtext, text, lineno, inliner):
123    """
124    Add a Sphinx role to handle numref references. Note, we can't convert
125    the link here because the doctree isn't build and the target information
126    isn't available.
127    """
128    # Add an identifier to distinguish numref from other references.
129    newnode = nodes.reference('',
130                              '',
131                              refuri='_local_numref_#%s' % text,
132                              internal=True)
133    return [newnode], []
134
135
136def process_numref(app, doctree, from_docname):
137    """
138    Process the numref nodes once the doctree has been built and prior to
139    writing the files. The processing involves replacing the numref with a
140    link plus text to indicate if it is a Figure or Table link.
141    """
142
143    # Iterate over the reference nodes in the doctree.
144    for node in doctree.traverse(nodes.reference):
145        target = node.get('refuri', '')
146
147        # Look for numref nodes.
148        if target.startswith('_local_numref_#'):
149            target = target.replace('_local_numref_#', '')
150
151            # Get the target label and link information from the Sphinx env.
152            data = app.builder.env.domains['std'].data
153            docname, label, _ = data['labels'].get(target, ('', '', ''))
154            relative_url = app.builder.get_relative_uri(from_docname, docname)
155
156            # Add a text label to the link.
157            if target.startswith('figure'):
158                caption = 'Figure'
159            elif target.startswith('table'):
160                caption = 'Table'
161            else:
162                caption = 'Link'
163
164            # New reference node with the updated link information.
165            newnode = nodes.reference('',
166                                      caption,
167                                      refuri='%s#%s' % (relative_url, label),
168                                      internal=True)
169            node.replace_self(newnode)
170
171
172def generate_overview_table(output_filename, table_id, section, table_name, title):
173    """
174    Function to generate the Overview Table from the ini files that define
175    the features for each driver.
176
177    The default features for the table and their order is defined by the
178    'default.ini' file.
179
180    """
181    # Default warning string.
182    warning = 'Warning generate_overview_table()'
183
184    # Get the default features and order from the 'default.ini' file.
185    ini_path = path_join(dirname(output_filename), 'features')
186    config = configparser.ConfigParser()
187    config.optionxform = str
188    config.read(path_join(ini_path, 'default.ini'))
189    default_features = config.items(section)
190
191    # Create a dict of the valid features to validate the other ini files.
192    valid_features = {}
193    max_feature_length = 0
194    for feature in default_features:
195        key = feature[0]
196        valid_features[key] = ' '
197        max_feature_length = max(max_feature_length, len(key))
198
199    # Get a list of driver ini files, excluding 'default.ini'.
200    ini_files = [basename(file) for file in listdir(ini_path)
201                 if file.endswith('.ini') and file != 'default.ini']
202    ini_files.sort()
203
204    # Build up a list of the table header names from the ini filenames.
205    pmd_names = []
206    for ini_filename in ini_files:
207        name = ini_filename[:-4]
208        name = name.replace('_vf', 'vf')
209        pmd_names.append(name)
210    if not pmd_names:
211        # Add an empty column if table is empty (required by RST syntax)
212        pmd_names.append(' ')
213
214    # Pad the table header names.
215    max_header_len = len(max(pmd_names, key=len))
216    header_names = []
217    for name in pmd_names:
218        if '_vec' in name:
219            pmd, vec = name.split('_')
220            name = '{0:{fill}{align}{width}}vec'.format(pmd,
221                    fill='.', align='<', width=max_header_len-3)
222        else:
223            name = '{0:{fill}{align}{width}}'.format(name,
224                    fill=' ', align='<', width=max_header_len)
225        header_names.append(name)
226
227    # Create a dict of the defined features for each driver from the ini files.
228    ini_data = {}
229    for ini_filename in ini_files:
230        config = configparser.ConfigParser()
231        config.optionxform = str
232        config.read(path_join(ini_path, ini_filename))
233
234        # Initialize the dict with the default.ini value.
235        ini_data[ini_filename] = valid_features.copy()
236
237        # Check for a section.
238        if not config.has_section(section):
239            continue
240
241        # Check for valid features names.
242        for name, value in config.items(section):
243            if name not in valid_features:
244                print("{}: Unknown feature '{}' in '{}'".format(warning,
245                                                                name,
246                                                                ini_filename),
247                                                                file=stderr)
248                if stop_on_error:
249                    raise Exception('Warning is treated as a failure')
250                continue
251
252            if value:
253                # Get the first letter only.
254                ini_data[ini_filename][name] = value[0]
255
256    # Print out the RST Driver Overview table from the ini file data.
257    outfile = open(output_filename, 'w')
258    num_cols = len(header_names)
259
260    print_table_css(outfile, table_id)
261    print('.. _' + table_name + ':', file=outfile)
262    print('.. table:: ' + table_name + '\n', file=outfile)
263    print_table_header(outfile, num_cols, header_names, title)
264    print_table_body(outfile, num_cols, ini_files, ini_data, default_features)
265
266
267def print_table_header(outfile, num_cols, header_names, title):
268    """ Print the RST table header. The header names are vertical. """
269    print_table_divider(outfile, num_cols)
270
271    line = ''
272    for name in header_names:
273        line += ' ' + name[0]
274
275    print_table_row(outfile, title, line)
276
277    for i in range(1, len(header_names[0])):
278        line = ''
279        for name in header_names:
280            line += ' ' + name[i]
281
282        print_table_row(outfile, '', line)
283
284    print_table_divider(outfile, num_cols)
285
286
287def print_table_body(outfile, num_cols, ini_files, ini_data, default_features):
288    """ Print out the body of the table. Each row is a NIC feature. """
289
290    for feature, _ in default_features:
291        line = ''
292
293        for ini_filename in ini_files:
294            line += ' ' + ini_data[ini_filename][feature]
295
296        print_table_row(outfile, feature, line)
297
298    print_table_divider(outfile, num_cols)
299
300
301def print_table_row(outfile, feature, line):
302    """ Print a single row of the table with fixed formatting. """
303    line = line.rstrip()
304    print('   {:<{}}{}'.format(feature, feature_str_len, line), file=outfile)
305
306
307def print_table_divider(outfile, num_cols):
308    """ Print the table divider line. """
309    line = ' '
310    column_dividers = ['='] * num_cols
311    line += ' '.join(column_dividers)
312
313    feature = '=' * feature_str_len
314
315    print_table_row(outfile, feature, line)
316
317
318def print_table_css(outfile, table_id):
319    template = """
320.. raw:: html
321
322   <style>
323      .wy-nav-content {
324         opacity: .99;
325      }
326      table#idx {
327         cursor: default;
328         overflow: hidden;
329      }
330      table#idx p {
331         margin: 0;
332         line-height: inherit;
333      }
334      table#idx th, table#idx td {
335         text-align: center;
336         border: solid 1px #ddd;
337      }
338      table#idx th {
339         padding: 0.5em 0;
340      }
341      table#idx th, table#idx th p {
342         font-size: 11px;
343         white-space: pre-wrap;
344         vertical-align: top;
345         min-width: 0.9em;
346      }
347      table#idx col:first-child {
348         width: 0;
349      }
350      table#idx th:first-child {
351         vertical-align: bottom;
352      }
353      table#idx td {
354         padding: 1px;
355      }
356      table#idx td, table#idx td p {
357         font-size: 11px;
358      }
359      table#idx td:first-child {
360         padding-left: 1em;
361         text-align: left;
362      }
363      table#idx tr:nth-child(2n-1) td {
364         background-color: rgba(210, 210, 210, 0.2);
365      }
366      table#idx th:not(:first-child):hover,
367      table#idx td:not(:first-child):hover {
368         position: relative;
369      }
370      table#idx th:not(:first-child):hover::after,
371      table#idx td:not(:first-child):hover::after {
372         content: '';
373         height: 6000px;
374         top: -3000px;
375         width: 100%;
376         left: 0;
377         position: absolute;
378         z-index: -1;
379         background-color: #ffb;
380      }
381      table#idx tr:hover td {
382         background-color: #ffb;
383      }
384   </style>
385"""
386    print(template.replace("idx", "id%d" % (table_id)), file=outfile)
387
388
389def setup(app):
390    table_file = dirname(__file__) + '/nics/overview_table.txt'
391    generate_overview_table(table_file, 1,
392                            'Features',
393                            'Features availability in networking drivers',
394                            'Feature')
395    table_file = dirname(__file__) + '/nics/rte_flow_items_table.txt'
396    generate_overview_table(table_file, 2,
397                            'rte_flow items',
398                            'rte_flow items availability in networking drivers',
399                            'Item')
400    table_file = dirname(__file__) + '/nics/rte_flow_actions_table.txt'
401    generate_overview_table(table_file, 3,
402                            'rte_flow actions',
403                            'rte_flow actions availability in networking drivers',
404                            'Action')
405    table_file = dirname(__file__) + '/cryptodevs/overview_feature_table.txt'
406    generate_overview_table(table_file, 1,
407                            'Features',
408                            'Features availability in crypto drivers',
409                            'Feature')
410    table_file = dirname(__file__) + '/cryptodevs/overview_cipher_table.txt'
411    generate_overview_table(table_file, 2,
412                            'Cipher',
413                            'Cipher algorithms in crypto drivers',
414                            'Cipher algorithm')
415    table_file = dirname(__file__) + '/cryptodevs/overview_auth_table.txt'
416    generate_overview_table(table_file, 3,
417                            'Auth',
418                            'Authentication algorithms in crypto drivers',
419                            'Authentication algorithm')
420    table_file = dirname(__file__) + '/cryptodevs/overview_aead_table.txt'
421    generate_overview_table(table_file, 4,
422                            'AEAD',
423                            'AEAD algorithms in crypto drivers',
424                            'AEAD algorithm')
425    table_file = dirname(__file__) + '/cryptodevs/overview_asym_table.txt'
426    generate_overview_table(table_file, 5,
427                            'Asymmetric',
428                            'Asymmetric algorithms in crypto drivers',
429                            'Asymmetric algorithm')
430    table_file = dirname(__file__) + '/cryptodevs/overview_os_table.txt'
431    generate_overview_table(table_file, 6,
432                            'OS',
433                            'Operating systems support for crypto drivers',
434                            'Operating system')
435    table_file = dirname(__file__) + '/compressdevs/overview_feature_table.txt'
436    generate_overview_table(table_file, 1,
437                            'Features',
438                            'Features availability in compression drivers',
439                            'Feature')
440    table_file = dirname(__file__) + '/regexdevs/overview_feature_table.txt'
441    generate_overview_table(table_file, 1,
442                            'Features',
443                            'Features availability in regex drivers',
444                            'Feature')
445    table_file = dirname(__file__) + '/vdpadevs/overview_feature_table.txt'
446    generate_overview_table(table_file, 1,
447                            'Features',
448                            'Features availability in vDPA drivers',
449                            'Feature')
450    table_file = dirname(__file__) + '/bbdevs/overview_feature_table.txt'
451    generate_overview_table(table_file, 1,
452                            'Features',
453                            'Features availability in bbdev drivers',
454                            'Feature')
455    table_file = dirname(__file__) + '/gpus/overview_feature_table.txt'
456    generate_overview_table(table_file, 1,
457                            'Features',
458                            'Features availability in GPU drivers',
459                            'Feature')
460    table_file = dirname(__file__) + '/eventdevs/overview_feature_table.txt'
461    generate_overview_table(table_file, 1,
462                            'Scheduling Features',
463                            'Features availability in eventdev drivers',
464                            'Feature')
465    table_file = dirname(__file__) + '/eventdevs/overview_rx_adptr_feature_table.txt'
466    generate_overview_table(table_file, 2,
467                            'Eth Rx adapter Features',
468                            'Features availability for Ethdev Rx adapters',
469                            'Feature')
470    table_file = dirname(__file__) + '/eventdevs/overview_tx_adptr_feature_table.txt'
471    generate_overview_table(table_file, 3,
472                            'Eth Tx adapter Features',
473                            'Features availability for Ethdev Tx adapters',
474                            'Feature')
475    table_file = dirname(__file__) + '/eventdevs/overview_crypto_adptr_feature_table.txt'
476    generate_overview_table(table_file, 4,
477                            'Crypto adapter Features',
478                            'Features availability for Crypto adapters',
479                            'Feature')
480    table_file = dirname(__file__) + '/eventdevs/overview_timer_adptr_feature_table.txt'
481    generate_overview_table(table_file, 5,
482                            'Timer adapter Features',
483                            'Features availability for Timer adapters',
484                            'Feature')
485
486    if Version(sphinx_version) < Version('1.3.1'):
487        print('Upgrade sphinx to version >= 1.3.1 for '
488              'improved Figure/Table number handling.',
489              file=stderr)
490        # Add a role to handle :numref: references.
491        app.add_role('numref', numref_role)
492        # Process the numref references once the doctree has been created.
493        app.connect('doctree-resolved', process_numref)
494
495    try:
496        # New function in sphinx 1.8
497        app.add_css_file('css/custom.css')
498    except:
499        app.add_stylesheet('css/custom.css')
500