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