doc: allow external build
[dpdk.git] / doc / guides / conf.py
1 #!/usr/bin/env python3
2 # SPDX-License-Identifier: BSD-3-Clause
3 # Copyright(c) 2010-2015 Intel Corporation
4
5 from docutils import nodes
6 from distutils.version import LooseVersion
7 from sphinx import __version__ as sphinx_version
8 from sphinx.highlighting import PygmentsBridge
9 from pygments.formatters.latex import LatexFormatter
10 from os import listdir
11 from os import environ
12 from os.path import basename
13 from os.path import dirname
14 from os.path import join as path_join
15 from sys import argv, stderr
16
17 import configparser
18
19 try:
20     import sphinx_rtd_theme
21
22     html_theme = "sphinx_rtd_theme"
23     html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
24 except:
25     print('Install the sphinx ReadTheDocs theme for improved html documentation '
26           'layout: https://sphinx-rtd-theme.readthedocs.io/',
27           file=stderr)
28     pass
29
30 stop_on_error = ('-W' in argv)
31
32 project = 'Data Plane Development Kit'
33 html_logo = '../logo/DPDK_logo_vertical_rev_small.png'
34 latex_logo = '../logo/DPDK_logo_horizontal_tag.png'
35 html_add_permalinks = ""
36 html_show_copyright = False
37 highlight_language = 'none'
38
39 release = environ.setdefault('DPDK_VERSION', "None")
40 version = release
41
42 master_doc = 'index'
43
44 # Maximum feature description string length
45 feature_str_len = 30
46
47 # Figures, tables and code-blocks automatically numbered if they have caption
48 numfig = True
49
50 latex_documents = [
51     ('index',
52      'doc.tex',
53      '',
54      '',
55      'manual')
56 ]
57
58 # Latex directives to be included directly in the latex/pdf docs.
59 custom_latex_preamble = r"""
60 \usepackage{textalpha}
61 \RecustomVerbatimEnvironment{Verbatim}{Verbatim}{xleftmargin=5mm}
62 \usepackage{etoolbox}
63 \robustify\(
64 \robustify\)
65 """
66
67 # Configuration for the latex/pdf docs.
68 latex_elements = {
69     'papersize': 'a4paper',
70     'pointsize': '11pt',
71     # remove blank pages
72     'classoptions': ',openany,oneside',
73     'babel': '\\usepackage[english]{babel}',
74     # customize Latex formatting
75     'preamble': custom_latex_preamble
76 }
77
78
79 # Override the default Latex formatter in order to modify the
80 # code/verbatim blocks.
81 class CustomLatexFormatter(LatexFormatter):
82     def __init__(self, **options):
83         super(CustomLatexFormatter, self).__init__(**options)
84         # Use the second smallest font size for code/verbatim blocks.
85         self.verboptions = r'formatcom=\footnotesize'
86
87 # Replace the default latex formatter.
88 PygmentsBridge.latex_formatter = CustomLatexFormatter
89
90 # Configuration for man pages
91 man_pages = [("testpmd_app_ug/run_app", "testpmd",
92               "tests for dpdk pmds", "", 1),
93              ("tools/pdump", "dpdk-pdump",
94               "enable packet capture on dpdk ports", "", 1),
95              ("tools/proc_info", "dpdk-procinfo",
96               "access dpdk port stats and memory info", "", 1),
97              ("tools/pmdinfo", "dpdk-pmdinfo",
98               "dump a PMDs hardware support info", "", 1),
99              ("tools/devbind", "dpdk-devbind",
100               "check device status and bind/unbind them from drivers", "", 8)]
101
102
103 # ####### :numref: fallback ########
104 # The following hook functions add some simple handling for the :numref:
105 # directive for Sphinx versions prior to 1.3.1. The functions replace the
106 # :numref: reference with a link to the target (for all Sphinx doc types).
107 # It doesn't try to label figures/tables.
108 def numref_role(reftype, rawtext, text, lineno, inliner):
109     """
110     Add a Sphinx role to handle numref references. Note, we can't convert
111     the link here because the doctree isn't build and the target information
112     isn't available.
113     """
114     # Add an identifier to distinguish numref from other references.
115     newnode = nodes.reference('',
116                               '',
117                               refuri='_local_numref_#%s' % text,
118                               internal=True)
119     return [newnode], []
120
121
122 def process_numref(app, doctree, from_docname):
123     """
124     Process the numref nodes once the doctree has been built and prior to
125     writing the files. The processing involves replacing the numref with a
126     link plus text to indicate if it is a Figure or Table link.
127     """
128
129     # Iterate over the reference nodes in the doctree.
130     for node in doctree.traverse(nodes.reference):
131         target = node.get('refuri', '')
132
133         # Look for numref nodes.
134         if target.startswith('_local_numref_#'):
135             target = target.replace('_local_numref_#', '')
136
137             # Get the target label and link information from the Sphinx env.
138             data = app.builder.env.domains['std'].data
139             docname, label, _ = data['labels'].get(target, ('', '', ''))
140             relative_url = app.builder.get_relative_uri(from_docname, docname)
141
142             # Add a text label to the link.
143             if target.startswith('figure'):
144                 caption = 'Figure'
145             elif target.startswith('table'):
146                 caption = 'Table'
147             else:
148                 caption = 'Link'
149
150             # New reference node with the updated link information.
151             newnode = nodes.reference('',
152                                       caption,
153                                       refuri='%s#%s' % (relative_url, label),
154                                       internal=True)
155             node.replace_self(newnode)
156
157
158 def generate_overview_table(output_filename, table_id, section, table_name, title):
159     """
160     Function to generate the Overview Table from the ini files that define
161     the features for each driver.
162
163     The default features for the table and their order is defined by the
164     'default.ini' file.
165
166     """
167     # Default warning string.
168     warning = 'Warning generate_overview_table()'
169
170     # Get the default features and order from the 'default.ini' file.
171     ini_path = path_join(dirname(output_filename), 'features')
172     config = configparser.ConfigParser()
173     config.optionxform = str
174     config.read(path_join(ini_path, 'default.ini'))
175     default_features = config.items(section)
176
177     # Create a dict of the valid features to validate the other ini files.
178     valid_features = {}
179     max_feature_length = 0
180     for feature in default_features:
181         key = feature[0]
182         valid_features[key] = ' '
183         max_feature_length = max(max_feature_length, len(key))
184
185     # Get a list of driver ini files, excluding 'default.ini'.
186     ini_files = [basename(file) for file in listdir(ini_path)
187                  if file.endswith('.ini') and file != 'default.ini']
188     ini_files.sort()
189
190     # Build up a list of the table header names from the ini filenames.
191     pmd_names = []
192     for ini_filename in ini_files:
193         name = ini_filename[:-4]
194         name = name.replace('_vf', 'vf')
195         pmd_names.append(name)
196
197     # Pad the table header names.
198     max_header_len = len(max(pmd_names, key=len))
199     header_names = []
200     for name in pmd_names:
201         if '_vec' in name:
202             pmd, vec = name.split('_')
203             name = '{0:{fill}{align}{width}}vec'.format(pmd,
204                     fill='.', align='<', width=max_header_len-3)
205         else:
206             name = '{0:{fill}{align}{width}}'.format(name,
207                     fill=' ', align='<', width=max_header_len)
208         header_names.append(name)
209
210     # Create a dict of the defined features for each driver from the ini files.
211     ini_data = {}
212     for ini_filename in ini_files:
213         config = configparser.ConfigParser()
214         config.optionxform = str
215         config.read(path_join(ini_path, ini_filename))
216
217         # Initialize the dict with the default.ini value.
218         ini_data[ini_filename] = valid_features.copy()
219
220         # Check for a valid ini section.
221         if not config.has_section(section):
222             print("{}: File '{}' has no [{}] secton".format(warning,
223                                                             ini_filename,
224                                                             section),
225                                                             file=stderr)
226             if stop_on_error:
227                 raise Exception('Warning is treated as a failure')
228             continue
229
230         # Check for valid features names.
231         for name, value in config.items(section):
232             if name not in valid_features:
233                 print("{}: Unknown feature '{}' in '{}'".format(warning,
234                                                                 name,
235                                                                 ini_filename),
236                                                                 file=stderr)
237                 if stop_on_error:
238                     raise Exception('Warning is treated as a failure')
239                 continue
240
241             if value:
242                 # Get the first letter only.
243                 ini_data[ini_filename][name] = value[0]
244
245     # Print out the RST Driver Overview table from the ini file data.
246     outfile = open(output_filename, 'w')
247     num_cols = len(header_names)
248
249     print_table_css(outfile, table_id)
250     print('.. table:: ' + table_name + '\n', file=outfile)
251     print_table_header(outfile, num_cols, header_names, title)
252     print_table_body(outfile, num_cols, ini_files, ini_data, default_features)
253
254
255 def print_table_header(outfile, num_cols, header_names, title):
256     """ Print the RST table header. The header names are vertical. """
257     print_table_divider(outfile, num_cols)
258
259     line = ''
260     for name in header_names:
261         line += ' ' + name[0]
262
263     print_table_row(outfile, title, line)
264
265     for i in range(1, len(header_names[0])):
266         line = ''
267         for name in header_names:
268             line += ' ' + name[i]
269
270         print_table_row(outfile, '', line)
271
272     print_table_divider(outfile, num_cols)
273
274
275 def print_table_body(outfile, num_cols, ini_files, ini_data, default_features):
276     """ Print out the body of the table. Each row is a NIC feature. """
277
278     for feature, _ in default_features:
279         line = ''
280
281         for ini_filename in ini_files:
282             line += ' ' + ini_data[ini_filename][feature]
283
284         print_table_row(outfile, feature, line)
285
286     print_table_divider(outfile, num_cols)
287
288
289 def print_table_row(outfile, feature, line):
290     """ Print a single row of the table with fixed formatting. """
291     line = line.rstrip()
292     print('   {:<{}}{}'.format(feature, feature_str_len, line), file=outfile)
293
294
295 def print_table_divider(outfile, num_cols):
296     """ Print the table divider line. """
297     line = ' '
298     column_dividers = ['='] * num_cols
299     line += ' '.join(column_dividers)
300
301     feature = '=' * feature_str_len
302
303     print_table_row(outfile, feature, line)
304
305
306 def print_table_css(outfile, table_id):
307     template = """
308 .. raw:: html
309
310    <style>
311       .wy-nav-content {
312          opacity: .99;
313       }
314       table#idx {
315          cursor: default;
316          overflow: hidden;
317       }
318       table#idx p {
319          margin: 0;
320          line-height: inherit;
321       }
322       table#idx th, table#idx td {
323          text-align: center;
324          border: solid 1px #ddd;
325       }
326       table#idx th {
327          padding: 0.5em 0;
328       }
329       table#idx th, table#idx th p {
330          font-size: 11px;
331          white-space: pre-wrap;
332          vertical-align: top;
333          min-width: 0.9em;
334       }
335       table#idx col:first-child {
336          width: 0;
337       }
338       table#idx th:first-child {
339          vertical-align: bottom;
340       }
341       table#idx td {
342          padding: 1px;
343       }
344       table#idx td, table#idx td p {
345          font-size: 11px;
346       }
347       table#idx td:first-child {
348          padding-left: 1em;
349          text-align: left;
350       }
351       table#idx tr:nth-child(2n-1) td {
352          background-color: rgba(210, 210, 210, 0.2);
353       }
354       table#idx th:not(:first-child):hover,
355       table#idx td:not(:first-child):hover {
356          position: relative;
357       }
358       table#idx th:not(:first-child):hover::after,
359       table#idx td:not(:first-child):hover::after {
360          content: '';
361          height: 6000px;
362          top: -3000px;
363          width: 100%;
364          left: 0;
365          position: absolute;
366          z-index: -1;
367          background-color: #ffb;
368       }
369       table#idx tr:hover td {
370          background-color: #ffb;
371       }
372    </style>
373 """
374     print(template.replace("idx", "id%d" % (table_id)), file=outfile)
375
376
377 def setup(app):
378     table_file = dirname(__file__) + '/nics/overview_table.txt'
379     generate_overview_table(table_file, 1,
380                             'Features',
381                             'Features availability in networking drivers',
382                             'Feature')
383     table_file = dirname(__file__) + '/cryptodevs/overview_feature_table.txt'
384     generate_overview_table(table_file, 1,
385                             'Features',
386                             'Features availability in crypto drivers',
387                             'Feature')
388     table_file = dirname(__file__) + '/cryptodevs/overview_cipher_table.txt'
389     generate_overview_table(table_file, 2,
390                             'Cipher',
391                             'Cipher algorithms in crypto drivers',
392                             'Cipher algorithm')
393     table_file = dirname(__file__) + '/cryptodevs/overview_auth_table.txt'
394     generate_overview_table(table_file, 3,
395                             'Auth',
396                             'Authentication algorithms in crypto drivers',
397                             'Authentication algorithm')
398     table_file = dirname(__file__) + '/cryptodevs/overview_aead_table.txt'
399     generate_overview_table(table_file, 4,
400                             'AEAD',
401                             'AEAD algorithms in crypto drivers',
402                             'AEAD algorithm')
403     table_file = dirname(__file__) + '/cryptodevs/overview_asym_table.txt'
404     generate_overview_table(table_file, 5,
405                             'Asymmetric',
406                             'Asymmetric algorithms in crypto drivers',
407                             'Asymmetric algorithm')
408     table_file = dirname(__file__) + '/compressdevs/overview_feature_table.txt'
409     generate_overview_table(table_file, 1,
410                             'Features',
411                             'Features availability in compression drivers',
412                             'Feature')
413     table_file = dirname(__file__) + '/regexdevs/overview_feature_table.txt'
414     generate_overview_table(table_file, 1,
415                             'Features',
416                             'Features availability in regex drivers',
417                             'Feature')
418     table_file = dirname(__file__) + '/vdpadevs/overview_feature_table.txt'
419     generate_overview_table(table_file, 1,
420                             'Features',
421                             'Features availability in vDPA drivers',
422                             'Feature')
423     table_file = dirname(__file__) + '/bbdevs/overview_feature_table.txt'
424     generate_overview_table(table_file, 1,
425                             'Features',
426                             'Features availability in bbdev drivers',
427                             'Feature')
428
429     if LooseVersion(sphinx_version) < LooseVersion('1.3.1'):
430         print('Upgrade sphinx to version >= 1.3.1 for '
431               'improved Figure/Table number handling.',
432               file=stderr)
433         # Add a role to handle :numref: references.
434         app.add_role('numref', numref_role)
435         # Process the numref references once the doctree has been created.
436         app.connect('doctree-resolved', process_numref)
437
438     try:
439         # New function in sphinx 1.8
440         app.add_css_file('css/custom.css')
441     except:
442         app.add_stylesheet('css/custom.css')