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