doc: generate NIC overview table from ini files
[dpdk.git] / doc / guides / conf.py
1 #   BSD LICENSE
2 #   Copyright(c) 2010-2015 Intel Corporation. All rights reserved.
3 #   All rights reserved.
4 #
5 #   Redistribution and use in source and binary forms, with or without
6 #   modification, are permitted provided that the following conditions
7 #   are met:
8 #
9 #   * Redistributions of source code must retain the above copyright
10 #   notice, this list of conditions and the following disclaimer.
11 #   * Redistributions in binary form must reproduce the above copyright
12 #   notice, this list of conditions and the following disclaimer in
13 #   the documentation and/or other materials provided with the
14 #   distribution.
15 #   * Neither the name of Intel Corporation nor the names of its
16 #   contributors may be used to endorse or promote products derived
17 #   from this software without specific prior written permission.
18 #
19 #   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 #   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 #   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 #   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 #   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 #   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 #   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 #   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 #   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 #   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 #   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
31 from __future__ import print_function
32 import subprocess
33 from docutils import nodes
34 from distutils.version import LooseVersion
35 from sphinx import __version__ as sphinx_version
36 from sphinx.highlighting import PygmentsBridge
37 from pygments.formatters.latex import LatexFormatter
38 from os import listdir
39 from os.path import basename
40 from os.path import dirname
41 from os.path import join as path_join
42
43 try:
44     # Python 2.
45     import ConfigParser as configparser
46 except:
47     # Python 3.
48     import configparser
49
50
51 project = 'Data Plane Development Kit'
52
53 if LooseVersion(sphinx_version) >= LooseVersion('1.3.1'):
54     html_theme = "sphinx_rtd_theme"
55 html_logo = '../logo/DPDK_logo_vertical_rev_small.png'
56 latex_logo = '../logo/DPDK_logo_horizontal_tag.png'
57 html_add_permalinks = ""
58 html_show_copyright = False
59 highlight_language = 'none'
60
61 version = subprocess.check_output(['make', '-sRrC', '../../', 'showversion']).decode('utf-8').rstrip()
62 release = version
63
64 master_doc = 'index'
65
66 # Figures, tables and code-blocks automatically numbered if they have caption
67 numfig = True
68
69 latex_documents = [
70     ('index',
71      'doc.tex',
72      '',
73      '',
74      'manual')
75 ]
76
77 # Latex directives to be included directly in the latex/pdf docs.
78 latex_preamble = r"""
79 \usepackage[utf8]{inputenc}
80 \usepackage[T1]{fontenc}
81 \usepackage{helvet}
82 \renewcommand{\familydefault}{\sfdefault}
83 \RecustomVerbatimEnvironment{Verbatim}{Verbatim}{xleftmargin=5mm}
84 """
85
86 # Configuration for the latex/pdf docs.
87 latex_elements = {
88     'papersize': 'a4paper',
89     'pointsize': '11pt',
90     # remove blank pages
91     'classoptions': ',openany,oneside',
92     'babel': '\\usepackage[english]{babel}',
93     # customize Latex formatting
94     'preamble': latex_preamble
95 }
96
97 # Override the default Latex formatter in order to modify the
98 # code/verbatim blocks.
99 class CustomLatexFormatter(LatexFormatter):
100     def __init__(self, **options):
101         super(CustomLatexFormatter, self).__init__(**options)
102         # Use the second smallest font size for code/verbatim blocks.
103         self.verboptions = r'formatcom=\footnotesize'
104
105 # Replace the default latex formatter.
106 PygmentsBridge.latex_formatter = CustomLatexFormatter
107
108 ######## :numref: fallback ########
109 # The following hook functions add some simple handling for the :numref:
110 # directive for Sphinx versions prior to 1.3.1. The functions replace the
111 # :numref: reference with a link to the target (for all Sphinx doc types).
112 # It doesn't try to label figures/tables.
113
114 def numref_role(reftype, rawtext, text, lineno, inliner):
115     """
116     Add a Sphinx role to handle numref references. Note, we can't convert
117     the link here because the doctree isn't build and the target information
118     isn't available.
119     """
120     # Add an identifier to distinguish numref from other references.
121     newnode = nodes.reference('',
122                               '',
123                               refuri='_local_numref_#%s' % text,
124                               internal=True)
125     return [newnode], []
126
127 def process_numref(app, doctree, from_docname):
128     """
129     Process the numref nodes once the doctree has been built and prior to
130     writing the files. The processing involves replacing the numref with a
131     link plus text to indicate if it is a Figure or Table link.
132     """
133
134     # Iterate over the reference nodes in the doctree.
135     for node in doctree.traverse(nodes.reference):
136         target = node.get('refuri', '')
137
138         # Look for numref nodes.
139         if target.startswith('_local_numref_#'):
140             target = target.replace('_local_numref_#', '')
141
142             # Get the target label and link information from the Sphinx env.
143             data = app.builder.env.domains['std'].data
144             docname, label, _ = data['labels'].get(target, ('', '', ''))
145             relative_url = app.builder.get_relative_uri(from_docname, docname)
146
147             # Add a text label to the link.
148             if target.startswith('figure'):
149                 caption = 'Figure'
150             elif target.startswith('table'):
151                 caption = 'Table'
152             else:
153                 caption = 'Link'
154
155             # New reference node with the updated link information.
156             newnode = nodes.reference('',
157                                       caption,
158                                       refuri='%s#%s' % (relative_url, label),
159                                       internal=True)
160             node.replace_self(newnode)
161
162
163 def generate_nic_overview_table(output_filename):
164     """
165     Function to generate the NIC Overview Table from the ini files that define
166     the features for each NIC.
167
168     The default features for the table and their order is defined by the
169     'default.ini' file.
170
171     """
172     # Default worning string.
173     warning = 'Warning generate_nic_overview_table()'
174
175     # Get the default features and order from the 'default.ini' file.
176     ini_path = path_join(dirname(output_filename), 'features')
177     config = configparser.ConfigParser()
178     config.optionxform = str
179     config.read(path_join(ini_path, 'default.ini'))
180     default_section = 'Features'
181     default_features = config.items(default_section)
182
183     # Create a dict of the valid features to validate the other ini files.
184     valid_features = {}
185     max_feature_length = 0
186     for feature in default_features:
187         key = feature[0]
188         valid_features[key] = ' '
189         max_feature_length = max(max_feature_length, len(key))
190
191     # Get a list of NIC ini files, excluding 'default.ini'.
192     ini_files = [basename(file) for file in listdir(ini_path)
193                  if file.endswith('.ini') and file != 'default.ini']
194     ini_files.sort()
195
196     # Build up a list of the table header names from the ini filenames.
197     header_names = []
198     for ini_filename in ini_files:
199         name = ini_filename[:-4]
200         name = name.replace('_vf', 'vf')
201
202         # Pad the table header names to match the existing format.
203         if '_vec' in name:
204             pmd, vec = name.split('_')
205             name = '{0:{fill}{align}7}vec'.format(pmd, fill='.', align='<')
206         else:
207             name = '{0:{fill}{align}10}'.format(name, fill=' ', align='<')
208
209         header_names.append(name)
210
211     # Create a dict of the defined features for each NIC from the ini files.
212     ini_data = {}
213     for ini_filename in ini_files:
214         config = configparser.ConfigParser()
215         config.optionxform = str
216         config.read(path_join(ini_path, ini_filename))
217
218         # Initialize the dict with the default.ini value.
219         ini_data[ini_filename] = valid_features.copy()
220
221         # Check for a valid ini section.
222         if not config.has_section(default_section):
223             print("{}: File '{}' has no [{}] secton".format(warning,
224                                                             ini_filename,
225                                                             default_section))
226             continue
227
228         # Check for valid features names.
229         for name, value in config.items(default_section):
230             if name not in valid_features:
231                 print("{}: Unknown feature '{}' in '{}'".format(warning,
232                                                                 name,
233                                                                 ini_filename))
234                 continue
235
236             if value is not '':
237                 # Get the first letter only.
238                 ini_data[ini_filename][name] = value[0]
239
240     # Print out the RST NIC Overview table from the ini file data.
241     outfile = open(output_filename, 'w')
242     num_cols = len(header_names)
243
244     print('.. table:: Features availability in networking drivers\n',
245           file=outfile)
246
247     print_table_header(outfile, num_cols, header_names)
248     print_table_body(outfile, num_cols, ini_files, ini_data, default_features)
249
250
251 def print_table_header(outfile, num_cols, header_names):
252     """ Print the RST table header. The header names are vertical. """
253     print_table_divider(outfile, num_cols)
254
255     line = ''
256     for name in header_names:
257         line += ' ' + name[0]
258
259     print_table_row(outfile, 'Feature', line)
260
261     for i in range(1, 10):
262         line = ''
263         for name in header_names:
264             line += ' ' + name[i]
265
266         print_table_row(outfile, '', line)
267
268     print_table_divider(outfile, num_cols)
269
270
271 def print_table_body(outfile, num_cols, ini_files, ini_data, default_features):
272     """ Print out the body of the table. Each row is a NIC feature. """
273
274     for feature, _ in default_features:
275         line = ''
276
277         for ini_filename in ini_files:
278             line += ' ' + ini_data[ini_filename][feature]
279
280         print_table_row(outfile, feature, line)
281
282     print_table_divider(outfile, num_cols)
283
284
285 def print_table_row(outfile, feature, line):
286     """ Print a single row of the table with fixed formatting. """
287     line = line.rstrip()
288     print('   {:<20}{}'.format(feature, line), file=outfile)
289
290
291 def print_table_divider(outfile, num_cols):
292     """ Print the table divider line. """
293     line = ' '
294     column_dividers = ['='] * num_cols
295     line += ' '.join(column_dividers)
296
297     feature = '=' * 20
298
299     print_table_row(outfile, feature, line)
300
301
302 def setup(app):
303     generate_nic_overview_table('doc/guides/nics/overview_table.txt')
304
305     if LooseVersion(sphinx_version) < LooseVersion('1.3.1'):
306         print('Upgrade sphinx to version >= 1.3.1 for '
307               'improved Figure/Table number handling.')
308         # Add a role to handle :numref: references.
309         app.add_role('numref', numref_role)
310         # Process the numref references once the doctree has been created.
311         app.connect('doctree-resolved', process_numref)