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