mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-11-11 23:47:24 +00:00
Merge branch '2021-01-22-doc-updates'
- Assorted documentation updates
This commit is contained in:
commit
b577bc1c5f
22 changed files with 1787 additions and 409 deletions
|
@ -106,7 +106,7 @@ cleandocs:
|
||||||
$(Q)$(MAKE) BUILDDIR=$(abspath $(BUILDDIR)) $(build)=doc/media clean
|
$(Q)$(MAKE) BUILDDIR=$(abspath $(BUILDDIR)) $(build)=doc/media clean
|
||||||
|
|
||||||
dochelp:
|
dochelp:
|
||||||
@echo ' Linux kernel internal documentation in different formats from ReST:'
|
@echo ' U-Boot documentation in different formats from ReST:'
|
||||||
@echo ' htmldocs - HTML'
|
@echo ' htmldocs - HTML'
|
||||||
@echo ' latexdocs - LaTeX'
|
@echo ' latexdocs - LaTeX'
|
||||||
@echo ' pdfdocs - PDF'
|
@echo ' pdfdocs - PDF'
|
||||||
|
|
124
doc/README.menu
124
doc/README.menu
|
@ -1,124 +0,0 @@
|
||||||
SPDX-License-Identifier: GPL-2.0+
|
|
||||||
/*
|
|
||||||
* Copyright 2010-2011 Calxeda, Inc.
|
|
||||||
*/
|
|
||||||
|
|
||||||
U-Boot provides a set of interfaces for creating and using simple, text
|
|
||||||
based menus. Menus are displayed as lists of labeled entries on the
|
|
||||||
console, and an entry can be selected by entering its label.
|
|
||||||
|
|
||||||
To use the menu code, enable CONFIG_MENU, and include "menu.h" where
|
|
||||||
the interfaces should be available.
|
|
||||||
|
|
||||||
Menus are composed of items. Each item has a key used to identify it in
|
|
||||||
the menu, and an opaque pointer to data controlled by the consumer.
|
|
||||||
|
|
||||||
If you want to show a menu, instead starting the shell, define
|
|
||||||
CONFIG_AUTOBOOT_MENU_SHOW. You have to code the int menu_show(int bootdelay)
|
|
||||||
function, which handle your menu. This function returns the remaining
|
|
||||||
bootdelay.
|
|
||||||
|
|
||||||
Interfaces
|
|
||||||
----------
|
|
||||||
#include "menu.h"
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Consumers of the menu interfaces will use a struct menu * as the
|
|
||||||
* handle for a menu. struct menu is only fully defined in menu.c,
|
|
||||||
* preventing consumers of the menu interfaces from accessing its
|
|
||||||
* contents directly.
|
|
||||||
*/
|
|
||||||
struct menu;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* NOTE: See comments in common/menu.c for more detailed documentation on
|
|
||||||
* these interfaces.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* menu_create() - Creates a menu handle with default settings
|
|
||||||
*/
|
|
||||||
struct menu *menu_create(char *title, int timeout, int prompt,
|
|
||||||
void (*item_data_print)(void *),
|
|
||||||
char *(*item_choice)(void *),
|
|
||||||
void *item_choice_data);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* menu_item_add() - Adds or replaces a menu item
|
|
||||||
*/
|
|
||||||
int menu_item_add(struct menu *m, char *item_key, void *item_data);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* menu_default_set() - Sets the default choice for the menu
|
|
||||||
*/
|
|
||||||
int menu_default_set(struct menu *m, char *item_key);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* menu_default_choice() - Set *choice to point to the default item's data
|
|
||||||
*/
|
|
||||||
int menu_default_choice(struct menu *m, void **choice);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* menu_get_choice() - Returns the user's selected menu entry, or the
|
|
||||||
* default if the menu is set to not prompt or the timeout expires.
|
|
||||||
*/
|
|
||||||
int menu_get_choice(struct menu *m, void **choice);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* menu_destroy() - frees the memory used by a menu and its items.
|
|
||||||
*/
|
|
||||||
int menu_destroy(struct menu *m);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* menu_display_statusline(struct menu *m);
|
|
||||||
* shows a statusline for every menu_display call.
|
|
||||||
*/
|
|
||||||
void menu_display_statusline(struct menu *m);
|
|
||||||
|
|
||||||
Example Code
|
|
||||||
------------
|
|
||||||
This example creates a menu that always prompts, and allows the user
|
|
||||||
to pick from a list of tools. The item key and data are the same.
|
|
||||||
|
|
||||||
#include "menu.h"
|
|
||||||
|
|
||||||
char *tools[] = {
|
|
||||||
"Hammer",
|
|
||||||
"Screwdriver",
|
|
||||||
"Nail gun",
|
|
||||||
NULL
|
|
||||||
};
|
|
||||||
|
|
||||||
char *pick_a_tool(void)
|
|
||||||
{
|
|
||||||
struct menu *m;
|
|
||||||
int i;
|
|
||||||
char *tool = NULL;
|
|
||||||
|
|
||||||
m = menu_create("Tools", 0, 1, NULL);
|
|
||||||
|
|
||||||
for(i = 0; tools[i]; i++) {
|
|
||||||
if (menu_item_add(m, tools[i], tools[i]) != 1) {
|
|
||||||
printf("failed to add item!");
|
|
||||||
menu_destroy(m);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (menu_get_choice(m, (void **)&tool) != 1)
|
|
||||||
printf("Problem picking tool!\n");
|
|
||||||
|
|
||||||
menu_destroy(m);
|
|
||||||
|
|
||||||
return tool;
|
|
||||||
}
|
|
||||||
|
|
||||||
void caller(void)
|
|
||||||
{
|
|
||||||
char *tool = pick_a_tool();
|
|
||||||
|
|
||||||
if (tool) {
|
|
||||||
printf("picked a tool: %s\n", tool);
|
|
||||||
use_tool(tool);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -133,8 +133,8 @@ B4420 has:
|
||||||
B4860QDS Default Settings
|
B4860QDS Default Settings
|
||||||
-------------------------
|
-------------------------
|
||||||
|
|
||||||
Switch Settings
|
B4860QDS Switch Settings
|
||||||
^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
.. code-block:: none
|
.. code-block:: none
|
||||||
|
|
||||||
|
@ -166,8 +166,8 @@ NOR boot::
|
||||||
B4420QDS Default Settings
|
B4420QDS Default Settings
|
||||||
-------------------------
|
-------------------------
|
||||||
|
|
||||||
Switch Settings
|
B4420QDS Switch Settings
|
||||||
^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
.. code-block:: none
|
.. code-block:: none
|
||||||
|
|
||||||
|
|
139
doc/conf.py
139
doc/conf.py
|
@ -16,6 +16,8 @@ import sys
|
||||||
import os
|
import os
|
||||||
import sphinx
|
import sphinx
|
||||||
|
|
||||||
|
from subprocess import check_output
|
||||||
|
|
||||||
# Get Sphinx version
|
# Get Sphinx version
|
||||||
major, minor, patch = sphinx.version_info[:3]
|
major, minor, patch = sphinx.version_info[:3]
|
||||||
|
|
||||||
|
@ -31,39 +33,98 @@ from load_config import loadConfig
|
||||||
# If your documentation needs a minimal Sphinx version, state it here.
|
# If your documentation needs a minimal Sphinx version, state it here.
|
||||||
needs_sphinx = '1.3'
|
needs_sphinx = '1.3'
|
||||||
|
|
||||||
latex_engine = 'xelatex'
|
|
||||||
|
|
||||||
# Add any Sphinx extension module names here, as strings. They can be
|
# Add any Sphinx extension module names here, as strings. They can be
|
||||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
||||||
# ones.
|
# ones.
|
||||||
extensions = ['kerneldoc', 'rstFlatTable', 'kernel_include', 'kfigure']
|
extensions = ['kerneldoc', 'rstFlatTable', 'kernel_include',
|
||||||
|
'kfigure', 'sphinx.ext.ifconfig', 'automarkup',
|
||||||
|
'maintainers_include', 'sphinx.ext.autosectionlabel',
|
||||||
|
'kernel_abi', 'kernel_feat']
|
||||||
|
|
||||||
#
|
#
|
||||||
# cdomain is badly broken in Sphinx 3+. Leaving it out generates *most*
|
# cdomain is badly broken in Sphinx 3+. Leaving it out generates *most*
|
||||||
# of the docs correctly, but not all.
|
# of the docs correctly, but not all. Scream bloody murder but allow
|
||||||
|
# the process to proceed; hopefully somebody will fix this properly soon.
|
||||||
#
|
#
|
||||||
if major >= 3:
|
if major >= 3:
|
||||||
|
sys.stderr.write('''WARNING: The kernel documentation build process
|
||||||
|
support for Sphinx v3.0 and above is brand new. Be prepared for
|
||||||
|
possible issues in the generated output.
|
||||||
|
''')
|
||||||
if (major > 3) or (minor > 0 or patch >= 2):
|
if (major > 3) or (minor > 0 or patch >= 2):
|
||||||
sys.stderr.write('''The build process with Sphinx 3+ is broken.
|
|
||||||
You will have to remove -W in doc/Makefile.
|
|
||||||
''')
|
|
||||||
# Sphinx c function parser is more pedantic with regards to type
|
# Sphinx c function parser is more pedantic with regards to type
|
||||||
# checking. Due to that, having macros at c:function cause problems.
|
# checking. Due to that, having macros at c:function cause problems.
|
||||||
# Those needed to be escaped by using c_id_attributes[] array
|
# Those needed to be scaped by using c_id_attributes[] array
|
||||||
c_id_attributes = [
|
c_id_attributes = [
|
||||||
|
# GCC Compiler types not parsed by Sphinx:
|
||||||
|
"__restrict__",
|
||||||
|
|
||||||
# include/linux/compiler.h
|
# include/linux/compiler_types.h:
|
||||||
|
"__iomem",
|
||||||
|
"__kernel",
|
||||||
|
"noinstr",
|
||||||
|
"notrace",
|
||||||
|
"__percpu",
|
||||||
|
"__rcu",
|
||||||
|
"__user",
|
||||||
|
|
||||||
|
# include/linux/compiler_attributes.h:
|
||||||
|
"__alias",
|
||||||
|
"__aligned",
|
||||||
|
"__aligned_largest",
|
||||||
|
"__always_inline",
|
||||||
|
"__assume_aligned",
|
||||||
|
"__cold",
|
||||||
|
"__attribute_const__",
|
||||||
|
"__copy",
|
||||||
|
"__pure",
|
||||||
|
"__designated_init",
|
||||||
|
"__visible",
|
||||||
|
"__printf",
|
||||||
|
"__scanf",
|
||||||
|
"__gnu_inline",
|
||||||
|
"__malloc",
|
||||||
|
"__mode",
|
||||||
|
"__no_caller_saved_registers",
|
||||||
|
"__noclone",
|
||||||
|
"__nonstring",
|
||||||
|
"__noreturn",
|
||||||
|
"__packed",
|
||||||
|
"__pure",
|
||||||
|
"__section",
|
||||||
|
"__always_unused",
|
||||||
"__maybe_unused",
|
"__maybe_unused",
|
||||||
|
"__used",
|
||||||
|
"__weak",
|
||||||
|
"noinline",
|
||||||
|
|
||||||
# include/efi.h
|
# include/efi.h
|
||||||
"EFIAPI",
|
"EFIAPI",
|
||||||
|
|
||||||
# include/efi_loader.h
|
# include/efi_loader.h
|
||||||
"__efi_runtime",
|
"__efi_runtime",
|
||||||
|
|
||||||
|
# include/linux/memblock.h:
|
||||||
|
"__init_memblock",
|
||||||
|
"__meminit",
|
||||||
|
|
||||||
|
# include/linux/init.h:
|
||||||
|
"__init",
|
||||||
|
"__ref",
|
||||||
|
|
||||||
|
# include/linux/linkage.h:
|
||||||
|
"asmlinkage",
|
||||||
]
|
]
|
||||||
|
|
||||||
else:
|
else:
|
||||||
extensions.append('cdomain')
|
extensions.append('cdomain')
|
||||||
|
if major == 1 and minor < 7:
|
||||||
|
sys.stderr.write('WARNING: Sphinx 1.7 or greater will be required as of '
|
||||||
|
'the v2021.04 release\n')
|
||||||
|
|
||||||
|
# Ensure that autosectionlabel will produce unique names
|
||||||
|
autosectionlabel_prefix_document = True
|
||||||
|
autosectionlabel_maxdepth = 2
|
||||||
|
|
||||||
# The name of the math extension changed on Sphinx 1.4
|
# The name of the math extension changed on Sphinx 1.4
|
||||||
if (major == 1 and minor > 3) or (major > 1):
|
if (major == 1 and minor > 3) or (major > 1):
|
||||||
|
@ -86,9 +147,9 @@ source_suffix = '.rst'
|
||||||
master_doc = 'index'
|
master_doc = 'index'
|
||||||
|
|
||||||
# General information about the project.
|
# General information about the project.
|
||||||
project = 'Das U-Boot'
|
project = 'The Linux Kernel'
|
||||||
copyright = 'The U-Boot development community'
|
copyright = 'The kernel development community'
|
||||||
author = 'The U-Boot development community'
|
author = 'The kernel development community'
|
||||||
|
|
||||||
# The version info for the project you're documenting, acts as replacement for
|
# The version info for the project you're documenting, acts as replacement for
|
||||||
# |version| and |release|, also used in various other places throughout the
|
# |version| and |release|, also used in various other places throughout the
|
||||||
|
@ -199,7 +260,7 @@ except ImportError:
|
||||||
|
|
||||||
# The name of an image file (relative to this directory) to place at the top
|
# The name of an image file (relative to this directory) to place at the top
|
||||||
# of the sidebar.
|
# of the sidebar.
|
||||||
html_logo = '../tools/logos/u-boot_logo.svg'
|
#html_logo = None
|
||||||
|
|
||||||
# The name of an image file (within the static path) to use as favicon of the
|
# The name of an image file (within the static path) to use as favicon of the
|
||||||
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
|
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
|
||||||
|
@ -229,7 +290,7 @@ html_context = {
|
||||||
|
|
||||||
# If true, SmartyPants will be used to convert quotes and dashes to
|
# If true, SmartyPants will be used to convert quotes and dashes to
|
||||||
# typographically correct entities.
|
# typographically correct entities.
|
||||||
#html_use_smartypants = True
|
html_use_smartypants = False
|
||||||
|
|
||||||
# Custom sidebar templates, maps document names to template names.
|
# Custom sidebar templates, maps document names to template names.
|
||||||
#html_sidebars = {}
|
#html_sidebars = {}
|
||||||
|
@ -279,7 +340,7 @@ html_context = {
|
||||||
#html_search_scorer = 'scorer.js'
|
#html_search_scorer = 'scorer.js'
|
||||||
|
|
||||||
# Output file base name for HTML help builder.
|
# Output file base name for HTML help builder.
|
||||||
htmlhelp_basename = 'TheUBootdoc'
|
htmlhelp_basename = 'TheLinuxKerneldoc'
|
||||||
|
|
||||||
# -- Options for LaTeX output ---------------------------------------------
|
# -- Options for LaTeX output ---------------------------------------------
|
||||||
|
|
||||||
|
@ -288,7 +349,7 @@ latex_elements = {
|
||||||
'papersize': 'a4paper',
|
'papersize': 'a4paper',
|
||||||
|
|
||||||
# The font size ('10pt', '11pt' or '12pt').
|
# The font size ('10pt', '11pt' or '12pt').
|
||||||
'pointsize': '8pt',
|
'pointsize': '11pt',
|
||||||
|
|
||||||
# Latex figure (float) alignment
|
# Latex figure (float) alignment
|
||||||
#'figure_align': 'htbp',
|
#'figure_align': 'htbp',
|
||||||
|
@ -301,13 +362,24 @@ latex_elements = {
|
||||||
'preamble': '''
|
'preamble': '''
|
||||||
% Use some font with UTF-8 support with XeLaTeX
|
% Use some font with UTF-8 support with XeLaTeX
|
||||||
\\usepackage{fontspec}
|
\\usepackage{fontspec}
|
||||||
\\setsansfont{DejaVu Serif}
|
\\setsansfont{DejaVu Sans}
|
||||||
\\setromanfont{DejaVu Sans}
|
\\setromanfont{DejaVu Serif}
|
||||||
\\setmonofont{DejaVu Sans Mono}
|
\\setmonofont{DejaVu Sans Mono}
|
||||||
|
|
||||||
'''
|
'''
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# At least one book (translations) may have Asian characters
|
||||||
|
# with are only displayed if xeCJK is used
|
||||||
|
|
||||||
|
cjk_cmd = check_output(['fc-list', '--format="%{family[0]}\n"']).decode('utf-8', 'ignore')
|
||||||
|
if cjk_cmd.find("Noto Sans CJK SC") >= 0:
|
||||||
|
print ("enabling CJK for LaTeX builder")
|
||||||
|
latex_elements['preamble'] += '''
|
||||||
|
% This is needed for translations
|
||||||
|
\\usepackage{xeCJK}
|
||||||
|
\\setCJKmainfont{Noto Sans CJK SC}
|
||||||
|
'''
|
||||||
|
|
||||||
# Fix reference escape troubles with Sphinx 1.4.x
|
# Fix reference escape troubles with Sphinx 1.4.x
|
||||||
if major == 1 and minor > 3:
|
if major == 1 and minor > 3:
|
||||||
latex_elements['preamble'] += '\\renewcommand*{\\DUrole}[2]{ #2 }\n'
|
latex_elements['preamble'] += '\\renewcommand*{\\DUrole}[2]{ #2 }\n'
|
||||||
|
@ -398,10 +470,23 @@ if major == 1 and minor < 6:
|
||||||
# author, documentclass [howto, manual, or own class]).
|
# author, documentclass [howto, manual, or own class]).
|
||||||
# Sorted in alphabetical order
|
# Sorted in alphabetical order
|
||||||
latex_documents = [
|
latex_documents = [
|
||||||
('index', 'u-boot-hacker-manual.tex', 'U-Boot Hacker Manual',
|
|
||||||
'The U-Boot development community', 'manual'),
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
# Add all other index files from Documentation/ subdirectories
|
||||||
|
for fn in os.listdir('.'):
|
||||||
|
doc = os.path.join(fn, "index")
|
||||||
|
if os.path.exists(doc + ".rst"):
|
||||||
|
has = False
|
||||||
|
for l in latex_documents:
|
||||||
|
if l[0] == doc:
|
||||||
|
has = True
|
||||||
|
break
|
||||||
|
if not has:
|
||||||
|
latex_documents.append((doc, fn + '.tex',
|
||||||
|
'Linux %s Documentation' % fn.capitalize(),
|
||||||
|
'The kernel development community',
|
||||||
|
'manual'))
|
||||||
|
|
||||||
# The name of an image file (relative to this directory) to place at the top of
|
# The name of an image file (relative to this directory) to place at the top of
|
||||||
# the title page.
|
# the title page.
|
||||||
#latex_logo = None
|
#latex_logo = None
|
||||||
|
@ -428,7 +513,7 @@ latex_documents = [
|
||||||
# One entry per manual page. List of tuples
|
# One entry per manual page. List of tuples
|
||||||
# (source start file, name, description, authors, manual section).
|
# (source start file, name, description, authors, manual section).
|
||||||
man_pages = [
|
man_pages = [
|
||||||
(master_doc, 'dasuboot', 'The U-Boot Documentation',
|
(master_doc, 'thelinuxkernel', 'The Linux Kernel Documentation',
|
||||||
[author], 1)
|
[author], 1)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -442,8 +527,8 @@ man_pages = [
|
||||||
# (source start file, target name, title, author,
|
# (source start file, target name, title, author,
|
||||||
# dir menu entry, description, category)
|
# dir menu entry, description, category)
|
||||||
texinfo_documents = [
|
texinfo_documents = [
|
||||||
(master_doc, 'DasUBoot', 'The U-Boot Documentation',
|
(master_doc, 'TheLinuxKernel', 'The Linux Kernel Documentation',
|
||||||
author, 'DasUBoot', 'One line description of project.',
|
author, 'TheLinuxKernel', 'One line description of project.',
|
||||||
'Miscellaneous'),
|
'Miscellaneous'),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -535,13 +620,13 @@ epub_exclude_files = ['search.html']
|
||||||
# Grouping the document tree into PDF files. List of tuples
|
# Grouping the document tree into PDF files. List of tuples
|
||||||
# (source start file, target name, title, author, options).
|
# (source start file, target name, title, author, options).
|
||||||
#
|
#
|
||||||
# See the Sphinx chapter of http://ralsina.me/static/manual.pdf
|
# See the Sphinx chapter of https://ralsina.me/static/manual.pdf
|
||||||
#
|
#
|
||||||
# FIXME: Do not add the index file here; the result will be too big. Adding
|
# FIXME: Do not add the index file here; the result will be too big. Adding
|
||||||
# multiple PDF files here actually tries to get the cross-referencing right
|
# multiple PDF files here actually tries to get the cross-referencing right
|
||||||
# *between* PDF files.
|
# *between* PDF files.
|
||||||
pdf_documents = [
|
pdf_documents = [
|
||||||
('uboot-documentation', u'U-Boot', u'U-Boot', u'J. Random Bozo'),
|
('kernel-documentation', u'Kernel', u'Kernel', u'J. Random Bozo'),
|
||||||
]
|
]
|
||||||
|
|
||||||
# kernel-doc extension configuration for running Sphinx directly (e.g. by Read
|
# kernel-doc extension configuration for running Sphinx directly (e.g. by Read
|
||||||
|
|
|
@ -3,13 +3,32 @@
|
||||||
Develop U-Boot
|
Develop U-Boot
|
||||||
==============
|
==============
|
||||||
|
|
||||||
|
Implementation
|
||||||
|
--------------
|
||||||
|
|
||||||
.. toctree::
|
.. toctree::
|
||||||
:maxdepth: 2
|
:maxdepth: 1
|
||||||
|
|
||||||
coccinelle
|
|
||||||
commands
|
commands
|
||||||
crash_dumps
|
|
||||||
global_data
|
global_data
|
||||||
logging
|
logging
|
||||||
|
menus
|
||||||
|
|
||||||
|
Debugging
|
||||||
|
---------
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
:maxdepth: 1
|
||||||
|
|
||||||
|
crash_dumps
|
||||||
trace
|
trace
|
||||||
|
|
||||||
|
Testing
|
||||||
|
-------
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
:maxdepth: 1
|
||||||
|
|
||||||
|
coccinelle
|
||||||
|
testing
|
||||||
|
py_testing
|
||||||
|
|
131
doc/develop/menus.rst
Normal file
131
doc/develop/menus.rst
Normal file
|
@ -0,0 +1,131 @@
|
||||||
|
.. SPDX-License-Identifier: GPL-2.0+
|
||||||
|
.. Copyright 2010-2011 Calxeda, Inc.
|
||||||
|
|
||||||
|
Menus
|
||||||
|
=====
|
||||||
|
|
||||||
|
U-Boot provides a set of interfaces for creating and using simple, text
|
||||||
|
based menus. Menus are displayed as lists of labeled entries on the
|
||||||
|
console, and an entry can be selected by entering its label.
|
||||||
|
|
||||||
|
To use the menu code, enable CONFIG_MENU, and include "menu.h" where
|
||||||
|
the interfaces should be available.
|
||||||
|
|
||||||
|
Menus are composed of items. Each item has a key used to identify it in
|
||||||
|
the menu, and an opaque pointer to data controlled by the consumer.
|
||||||
|
|
||||||
|
If you want to show a menu, instead starting the shell, define
|
||||||
|
CONFIG_AUTOBOOT_MENU_SHOW. You have to code the int menu_show(int bootdelay)
|
||||||
|
function, which handle your menu. This function returns the remaining
|
||||||
|
bootdelay.
|
||||||
|
|
||||||
|
Interfaces
|
||||||
|
----------
|
||||||
|
|
||||||
|
.. code-block:: c
|
||||||
|
|
||||||
|
#include "menu.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Consumers of the menu interfaces will use a struct menu * as the
|
||||||
|
* handle for a menu. struct menu is only fully defined in menu.c,
|
||||||
|
* preventing consumers of the menu interfaces from accessing its
|
||||||
|
* contents directly.
|
||||||
|
*/
|
||||||
|
struct menu;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* NOTE: See comments in common/menu.c for more detailed documentation on
|
||||||
|
* these interfaces.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* menu_create() - Creates a menu handle with default settings
|
||||||
|
*/
|
||||||
|
struct menu *menu_create(char *title, int timeout, int prompt,
|
||||||
|
void (*item_data_print)(void *),
|
||||||
|
char *(*item_choice)(void *),
|
||||||
|
void *item_choice_data);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* menu_item_add() - Adds or replaces a menu item
|
||||||
|
*/
|
||||||
|
int menu_item_add(struct menu *m, char *item_key, void *item_data);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* menu_default_set() - Sets the default choice for the menu
|
||||||
|
*/
|
||||||
|
int menu_default_set(struct menu *m, char *item_key);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* menu_default_choice() - Set *choice to point to the default item's data
|
||||||
|
*/
|
||||||
|
int menu_default_choice(struct menu *m, void **choice);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* menu_get_choice() - Returns the user's selected menu entry, or the
|
||||||
|
* default if the menu is set to not prompt or the timeout expires.
|
||||||
|
*/
|
||||||
|
int menu_get_choice(struct menu *m, void **choice);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* menu_destroy() - frees the memory used by a menu and its items.
|
||||||
|
*/
|
||||||
|
int menu_destroy(struct menu *m);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* menu_display_statusline(struct menu *m);
|
||||||
|
* shows a statusline for every menu_display call.
|
||||||
|
*/
|
||||||
|
void menu_display_statusline(struct menu *m);
|
||||||
|
|
||||||
|
Example Code
|
||||||
|
------------
|
||||||
|
|
||||||
|
This example creates a menu that always prompts, and allows the user
|
||||||
|
to pick from a list of tools. The item key and data are the same.
|
||||||
|
|
||||||
|
.. code-block:: c
|
||||||
|
|
||||||
|
#include "menu.h"
|
||||||
|
|
||||||
|
char *tools[] = {
|
||||||
|
"Hammer",
|
||||||
|
"Screwdriver",
|
||||||
|
"Nail gun",
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
char *pick_a_tool(void)
|
||||||
|
{
|
||||||
|
struct menu *m;
|
||||||
|
int i;
|
||||||
|
char *tool = NULL;
|
||||||
|
|
||||||
|
m = menu_create("Tools", 0, 1, NULL);
|
||||||
|
|
||||||
|
for(i = 0; tools[i]; i++) {
|
||||||
|
if (menu_item_add(m, tools[i], tools[i]) != 1) {
|
||||||
|
printf("failed to add item!");
|
||||||
|
menu_destroy(m);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (menu_get_choice(m, (void **)&tool) != 1)
|
||||||
|
printf("Problem picking tool!\n");
|
||||||
|
|
||||||
|
menu_destroy(m);
|
||||||
|
|
||||||
|
return tool;
|
||||||
|
}
|
||||||
|
|
||||||
|
void caller(void)
|
||||||
|
{
|
||||||
|
char *tool = pick_a_tool();
|
||||||
|
|
||||||
|
if (tool) {
|
||||||
|
printf("picked a tool: %s\n", tool);
|
||||||
|
use_tool(tool);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,8 @@
|
||||||
# U-Boot pytest suite
|
U-Boot pytest suite
|
||||||
|
===================
|
||||||
|
|
||||||
## Introduction
|
Introduction
|
||||||
|
------------
|
||||||
|
|
||||||
This tool aims to test U-Boot by executing U-Boot shell commands using the
|
This tool aims to test U-Boot by executing U-Boot shell commands using the
|
||||||
console interface. A single top-level script exists to execute or attach to the
|
console interface. A single top-level script exists to execute or attach to the
|
||||||
|
@ -14,40 +16,42 @@ results. Advantages of this approach are:
|
||||||
flexible than writing it all in C.
|
flexible than writing it all in C.
|
||||||
- It is reasonably simple to interact with U-Boot in this way.
|
- It is reasonably simple to interact with U-Boot in this way.
|
||||||
|
|
||||||
## Requirements
|
Requirements
|
||||||
|
------------
|
||||||
|
|
||||||
The test suite is implemented using pytest. Interaction with the U-Boot console
|
The test suite is implemented using pytest. Interaction with the U-Boot console
|
||||||
involves executing some binary and interacting with its stdin/stdout. You will
|
involves executing some binary and interacting with its stdin/stdout. You will
|
||||||
need to implement various "hook" scripts that are called by the test suite at
|
need to implement various "hook" scripts that are called by the test suite at
|
||||||
the appropriate time.
|
the appropriate time.
|
||||||
|
|
||||||
In order to run the testsuite at a minimum we require that both python3 and
|
In order to run the test suite at a minimum we require that both Python 3 and
|
||||||
pip for python3 be installed. All of the required python modules are
|
pip for Python 3 are installed. All of the required python modules are
|
||||||
described in the requirements.txt file in this directory and can be installed
|
described in the requirements.txt file in the /test/py/ directory and can be
|
||||||
with the command ```pip install -r requirements.txt```
|
installed via the command
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
pip install -r requirements.txt
|
||||||
|
|
||||||
In order to execute certain tests on their supported platforms other tools
|
In order to execute certain tests on their supported platforms other tools
|
||||||
will be required. The following is an incomplete list:
|
will be required. The following is an incomplete list:
|
||||||
|
|
||||||
| Package |
|
* gdisk
|
||||||
| -------------- |
|
* dfu-util
|
||||||
| gdisk |
|
* dtc
|
||||||
| dfu-util |
|
* openssl
|
||||||
| dtc |
|
* sudo OR guestmount
|
||||||
| openssl |
|
* e2fsprogs
|
||||||
| sudo OR guestmount |
|
* util-linux
|
||||||
| e2fsprogs |
|
* coreutils
|
||||||
| util-linux |
|
* dosfstools
|
||||||
| coreutils |
|
* efitools
|
||||||
| dosfstools |
|
* mount
|
||||||
| efitools |
|
* mtools
|
||||||
| mount |
|
* sbsigntool
|
||||||
| mtools |
|
* udisks2
|
||||||
| sbsigntool |
|
|
||||||
| udisks2 |
|
|
||||||
|
|
||||||
|
Please use the appropriate commands for your distribution to match these tools
|
||||||
Please use the apporirate commands for your distribution to match these tools
|
|
||||||
up with the package that provides them.
|
up with the package that provides them.
|
||||||
|
|
||||||
The test script supports either:
|
The test script supports either:
|
||||||
|
@ -58,28 +62,31 @@ The test script supports either:
|
||||||
physical board, attach to the board's console stream, and reset the board.
|
physical board, attach to the board's console stream, and reset the board.
|
||||||
Further details are described later.
|
Further details are described later.
|
||||||
|
|
||||||
### Using `virtualenv` to provide requirements
|
Using `virtualenv` to provide requirements
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
The recommended way to run the test suite, in order to ensure reproducibility
|
The recommended way to run the test suite, in order to ensure reproducibility
|
||||||
is to use `virtualenv` to set up the necessary environment. This can be done
|
is to use `virtualenv` to set up the necessary environment. This can be done
|
||||||
via the following commands:
|
via the following commands:
|
||||||
|
|
||||||
```bash
|
|
||||||
$ cd /path/to/u-boot
|
|
||||||
$ sudo apt-get install python3 python3-virtualenv
|
|
||||||
$ virtualenv -p /usr/bin/python3 venv
|
|
||||||
$ . ./venv/bin/activate
|
|
||||||
$ pip install -r test/py/requirements.txt
|
|
||||||
```
|
|
||||||
|
|
||||||
## Testing sandbox
|
.. code-block:: console
|
||||||
|
|
||||||
To run the testsuite on the sandbox port (U-Boot built as a native user-space
|
$ cd /path/to/u-boot
|
||||||
|
$ sudo apt-get install python3 python3-virtualenv
|
||||||
|
$ virtualenv -p /usr/bin/python3 venv
|
||||||
|
$ . ./venv/bin/activate
|
||||||
|
$ pip install -r test/py/requirements.txt
|
||||||
|
|
||||||
|
Testing sandbox
|
||||||
|
---------------
|
||||||
|
|
||||||
|
To run the test suite on the sandbox port (U-Boot built as a native user-space
|
||||||
application), simply execute:
|
application), simply execute:
|
||||||
|
|
||||||
```
|
.. code-block:: bash
|
||||||
./test/py/test.py --bd sandbox --build
|
|
||||||
```
|
./test/py/test.py --bd sandbox --build
|
||||||
|
|
||||||
The `--bd` option tells the test suite which board type is being tested. This
|
The `--bd` option tells the test suite which board type is being tested. This
|
||||||
lets the test suite know which features the board has, and hence exactly what
|
lets the test suite know which features the board has, and hence exactly what
|
||||||
|
@ -95,7 +102,8 @@ will be written to `${build_dir}/test-log.html`. This is best viewed in a web
|
||||||
browser, but may be read directly as plain text, perhaps with the aid of the
|
browser, but may be read directly as plain text, perhaps with the aid of the
|
||||||
`html2text` utility.
|
`html2text` utility.
|
||||||
|
|
||||||
### Testing under a debugger
|
Testing under a debugger
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
If you need to run sandbox under a debugger, you may pass the command-line
|
If you need to run sandbox under a debugger, you may pass the command-line
|
||||||
option `--gdbserver COMM`. This causes two things to happens:
|
option `--gdbserver COMM`. This causes two things to happens:
|
||||||
|
@ -111,19 +119,21 @@ option `--gdbserver COMM`. This causes two things to happens:
|
||||||
A usage example is:
|
A usage example is:
|
||||||
|
|
||||||
Window 1:
|
Window 1:
|
||||||
```shell
|
|
||||||
./test/py/test.py --bd sandbox --gdbserver localhost:1234
|
.. code-block:: bash
|
||||||
```
|
|
||||||
|
./test/py/test.py --bd sandbox --gdbserver localhost:1234
|
||||||
|
|
||||||
Window 2:
|
Window 2:
|
||||||
```shell
|
|
||||||
gdb ./build-sandbox/u-boot -ex 'target remote localhost:1234'
|
.. code-block:: bash
|
||||||
```
|
|
||||||
|
gdb ./build-sandbox/u-boot -ex 'target remote localhost:1234'
|
||||||
|
|
||||||
Alternatively, you could leave off the `-ex` option and type the command
|
Alternatively, you could leave off the `-ex` option and type the command
|
||||||
manually into gdb once it starts.
|
manually into gdb once it starts.
|
||||||
|
|
||||||
You can use any debugger you wish, so long as it speaks the gdb remote
|
You can use any debugger you wish, as long as it speaks the gdb remote
|
||||||
protocol, or any graphical wrapper around gdb.
|
protocol, or any graphical wrapper around gdb.
|
||||||
|
|
||||||
Some tests deliberately cause the sandbox process to exit, e.g. to test the
|
Some tests deliberately cause the sandbox process to exit, e.g. to test the
|
||||||
|
@ -132,30 +142,39 @@ to attach the debugger to the new sandbox instance. If these tests are not
|
||||||
relevant to your debugging session, you can skip them using pytest's -k
|
relevant to your debugging session, you can skip them using pytest's -k
|
||||||
command-line option; see the next section.
|
command-line option; see the next section.
|
||||||
|
|
||||||
## Command-line options
|
Command-line options
|
||||||
|
--------------------
|
||||||
|
|
||||||
- `--board-type`, `--bd`, `-B` set the type of the board to be tested. For
|
--board-type, --bd, -B
|
||||||
example, `sandbox` or `seaboard`.
|
set the type of the board to be tested. For example, `sandbox` or `seaboard`.
|
||||||
- `--board-identity`, `--id` set the identity of the board to be tested.
|
|
||||||
This allows differentiation between multiple instances of the same type of
|
--board-identity`, --id
|
||||||
physical board that are attached to the same host machine. This parameter is
|
sets the identity of the board to be tested. This allows differentiation
|
||||||
not interpreted by the test script in any way, but rather is simply passed
|
between multiple instances of the same type of physical board that are
|
||||||
to the hook scripts described below, and may be used in any site-specific
|
attached to the same host machine. This parameter is not interpreted by th
|
||||||
way deemed necessary.
|
test script in any way, but rather is simply passed to the hook scripts
|
||||||
- `--build` indicates that the test script should compile U-Boot itself
|
described below, and may be used in any site-specific way deemed necessary.
|
||||||
before running the tests. If using this option, make sure that any
|
|
||||||
environment variables required by the build process are already set, such as
|
--build
|
||||||
`$CROSS_COMPILE`.
|
indicates that the test script should compile U-Boot itself before running
|
||||||
- `--buildman` indicates that `--build` should use buildman to build U-Boot.
|
the tests. If using this option, make sure that any environment variables
|
||||||
There is no need to set $CROSS_COMPILE` in this case since buildman handles
|
required by the build process are already set, such as `$CROSS_COMPILE`.
|
||||||
it.
|
|
||||||
- `--build-dir` sets the directory containing the compiled U-Boot binaries.
|
--buildman
|
||||||
If omitted, this is `${source_dir}/build-${board_type}`.
|
indicates that `--build` should use buildman to build U-Boot. There is no need
|
||||||
- `--result-dir` sets the directory to write results, such as log files,
|
to set $CROSS_COMPILE` in this case since buildman handles it.
|
||||||
into. If omitted, the build directory is used.
|
|
||||||
- `--persistent-data-dir` sets the directory used to store persistent test
|
--build-dir
|
||||||
data. This is test data that may be re-used across test runs, such as file-
|
sets the directory containing the compiled U-Boot binaries. If omitted, this
|
||||||
system images.
|
is `${source_dir}/build-${board_type}`.
|
||||||
|
|
||||||
|
--result-dir
|
||||||
|
sets the directory to write results, such as log files, into.
|
||||||
|
If omitted, the build directory is used.
|
||||||
|
|
||||||
|
--persistent-data-dir
|
||||||
|
sets the directory used to store persistent test data. This is test data that
|
||||||
|
may be re-used across test runs, such as file-system images.
|
||||||
|
|
||||||
`pytest` also implements a number of its own command-line options. Commonly used
|
`pytest` also implements a number of its own command-line options. Commonly used
|
||||||
options are mentioned below. Please see `pytest` documentation for complete
|
options are mentioned below. Please see `pytest` documentation for complete
|
||||||
|
@ -163,21 +182,26 @@ details. Execute `py.test --version` for a brief summary. Note that U-Boot's
|
||||||
test.py script passes all command-line arguments directly to `pytest` for
|
test.py script passes all command-line arguments directly to `pytest` for
|
||||||
processing.
|
processing.
|
||||||
|
|
||||||
- `-k` selects which tests to run. The default is to run all known tests. This
|
-k
|
||||||
|
selects which tests to run. The default is to run all known tests. This
|
||||||
option takes a single argument which is used to filter test names. Simple
|
option takes a single argument which is used to filter test names. Simple
|
||||||
logical operators are supported. For example:
|
logical operators are supported. For example:
|
||||||
- `'ums'` runs only tests with "ums" in their name.
|
|
||||||
- `'ut_dm'` runs only tests with "ut_dm" in their name. Note that in this
|
- `'-k ums'` runs only tests with "ums" in their name.
|
||||||
|
- `'-k ut_dm'` runs only tests with "ut_dm" in their name. Note that in this
|
||||||
case, "ut_dm" is a parameter to a test rather than the test name. The full
|
case, "ut_dm" is a parameter to a test rather than the test name. The full
|
||||||
test name is e.g. "test_ut[ut_dm_leak]".
|
test name is e.g. "test_ut[ut_dm_leak]".
|
||||||
- `'not reset'` runs everything except tests with "reset" in their name.
|
- `'-k not reset'` runs everything except tests with "reset" in their name.
|
||||||
- `'ut or hush'` runs only tests with "ut" or "hush" in their name.
|
- `'-k ut or hush'` runs only tests with "ut" or "hush" in their name.
|
||||||
- `'not (ut or hush)'` runs everything except tests with "ut" or "hush" in
|
- `'-k not (ut or hush)'` runs everything except tests with "ut" or "hush" in
|
||||||
their name.
|
their name.
|
||||||
- `-s` prevents pytest from hiding a test's stdout. This allows you to see
|
|
||||||
|
-s
|
||||||
|
prevents pytest from hiding a test's stdout. This allows you to see
|
||||||
U-Boot's console log in real time on pytest's stdout.
|
U-Boot's console log in real time on pytest's stdout.
|
||||||
|
|
||||||
## Testing real hardware
|
Testing real hardware
|
||||||
|
---------------------
|
||||||
|
|
||||||
The tools and techniques used to interact with real hardware will vary
|
The tools and techniques used to interact with real hardware will vary
|
||||||
radically between different host and target systems, and the whims of the user.
|
radically between different host and target systems, and the whims of the user.
|
||||||
|
@ -187,9 +211,11 @@ via `$PATH`. These scripts implement certain actions on behalf of the test
|
||||||
suite. This keeps the test suite simple and isolated from system variances
|
suite. This keeps the test suite simple and isolated from system variances
|
||||||
unrelated to U-Boot features.
|
unrelated to U-Boot features.
|
||||||
|
|
||||||
### Hook scripts
|
Hook scripts
|
||||||
|
~~~~~~~~~~~~
|
||||||
|
|
||||||
#### Environment variables
|
Environment variables
|
||||||
|
'''''''''''''''''''''
|
||||||
|
|
||||||
The following environment variables are set when running hook scripts:
|
The following environment variables are set when running hook scripts:
|
||||||
|
|
||||||
|
@ -202,16 +228,18 @@ The following environment variables are set when running hook scripts:
|
||||||
- `UBOOT_RESULT_DIR` the test result directory.
|
- `UBOOT_RESULT_DIR` the test result directory.
|
||||||
- `UBOOT_PERSISTENT_DATA_DIR` the test persistent data directory.
|
- `UBOOT_PERSISTENT_DATA_DIR` the test persistent data directory.
|
||||||
|
|
||||||
#### `u-boot-test-console`
|
u-boot-test-console
|
||||||
|
'''''''''''''''''''
|
||||||
|
|
||||||
This script provides access to the U-Boot console. The script's stdin/stdout
|
This script provides access to the U-Boot console. The script's stdin/stdout
|
||||||
should be connected to the board's console. This process should continue to run
|
should be connected to the board's console. This process should continue to run
|
||||||
indefinitely, until killed. The test suite will run this script in parallel
|
indefinitely, until killed. The test suite will run this script in parallel
|
||||||
with all other hooks.
|
with all other hooks.
|
||||||
|
|
||||||
This script may be implemented e.g. by exec()ing `cu`, `kermit`, `conmux`, etc.
|
This script may be implemented e.g. by executing `cu`, `kermit`, `conmux`, etc.
|
||||||
|
via exec().
|
||||||
|
|
||||||
If you are able to run U-Boot under a hardware simulator such as qemu, then
|
If you are able to run U-Boot under a hardware simulator such as QEMU, then
|
||||||
you would likely spawn that simulator from this script. However, note that
|
you would likely spawn that simulator from this script. However, note that
|
||||||
`u-boot-test-reset` may be called multiple times per test script run, and must
|
`u-boot-test-reset` may be called multiple times per test script run, and must
|
||||||
cause U-Boot to start execution from scratch each time. Hopefully your
|
cause U-Boot to start execution from scratch each time. Hopefully your
|
||||||
|
@ -219,10 +247,11 @@ simulator includes a virtual reset button! If not, you can launch the
|
||||||
simulator from `u-boot-test-reset` instead, while arranging for this console
|
simulator from `u-boot-test-reset` instead, while arranging for this console
|
||||||
process to always communicate with the current simulator instance.
|
process to always communicate with the current simulator instance.
|
||||||
|
|
||||||
#### `u-boot-test-flash`
|
u-boot-test-flash
|
||||||
|
'''''''''''''''''
|
||||||
|
|
||||||
Prior to running the test suite against a board, some arrangement must be made
|
Prior to running the test suite against a board, some arrangement must be made
|
||||||
so that the board executes the particular U-Boot binary to be tested. Often,
|
so that the board executes the particular U-Boot binary to be tested. Often
|
||||||
this involves writing the U-Boot binary to the board's flash ROM. The test
|
this involves writing the U-Boot binary to the board's flash ROM. The test
|
||||||
suite calls this hook script for that purpose.
|
suite calls this hook script for that purpose.
|
||||||
|
|
||||||
|
@ -248,7 +277,8 @@ hook script appropriately.
|
||||||
This script will typically be implemented by calling out to some SoC- or
|
This script will typically be implemented by calling out to some SoC- or
|
||||||
board-specific vendor flashing utility.
|
board-specific vendor flashing utility.
|
||||||
|
|
||||||
#### `u-boot-test-reset`
|
u-boot-test-reset
|
||||||
|
'''''''''''''''''
|
||||||
|
|
||||||
Whenever the test suite needs to reset the target board, this script is
|
Whenever the test suite needs to reset the target board, this script is
|
||||||
executed. This is guaranteed to happen at least once, prior to executing the
|
executed. This is guaranteed to happen at least once, prior to executing the
|
||||||
|
@ -261,20 +291,22 @@ relay or electronic switch attached to the board's reset signal.
|
||||||
|
|
||||||
The semantics of this script require that when it is executed, U-Boot will
|
The semantics of this script require that when it is executed, U-Boot will
|
||||||
start running from scratch. If the U-Boot binary to be tested has been written
|
start running from scratch. If the U-Boot binary to be tested has been written
|
||||||
to flash, pulsing the board's reset signal is likely all this script need do.
|
to flash, pulsing the board's reset signal is likely all this script needs to
|
||||||
However, in some scenarios, this script may perform other actions. For
|
do. However, in some scenarios, this script may perform other actions. For
|
||||||
example, it may call out to some SoC- or board-specific vendor utility in order
|
example, it may call out to some SoC- or board-specific vendor utility in order
|
||||||
to download the U-Boot binary directly into RAM and execute it. This would
|
to download the U-Boot binary directly into RAM and execute it. This would
|
||||||
avoid the need for `u-boot-test-flash` to actually write U-Boot to flash, thus
|
avoid the need for `u-boot-test-flash` to actually write U-Boot to flash, thus
|
||||||
saving wear on the flash chip(s).
|
saving wear on the flash chip(s).
|
||||||
|
|
||||||
#### Examples
|
Examples
|
||||||
|
''''''''
|
||||||
|
|
||||||
https://github.com/swarren/uboot-test-hooks contains some working example hook
|
https://github.com/swarren/uboot-test-hooks contains some working example hook
|
||||||
scripts, and may be useful as a reference when implementing hook scripts for
|
scripts, and may be useful as a reference when implementing hook scripts for
|
||||||
your platform. These scripts are not considered part of U-Boot itself.
|
your platform. These scripts are not considered part of U-Boot itself.
|
||||||
|
|
||||||
### Board-type-specific configuration
|
Board-type-specific configuration
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
Each board has a different configuration and behaviour. Many of these
|
Each board has a different configuration and behaviour. Many of these
|
||||||
differences can be automatically detected by parsing the `.config` file in the
|
differences can be automatically detected by parsing the `.config` file in the
|
||||||
|
@ -286,7 +318,8 @@ defined in these modules is available for use by any test function. The data
|
||||||
contained in these scripts must be purely derived from U-Boot source code.
|
contained in these scripts must be purely derived from U-Boot source code.
|
||||||
Hence, these configuration files are part of the U-Boot source tree too.
|
Hence, these configuration files are part of the U-Boot source tree too.
|
||||||
|
|
||||||
### Execution environment configuration
|
Execution environment configuration
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
Each user's hardware setup may enable testing different subsets of the features
|
Each user's hardware setup may enable testing different subsets of the features
|
||||||
implemented by a particular board's configuration of U-Boot. For example, a
|
implemented by a particular board's configuration of U-Boot. For example, a
|
||||||
|
@ -304,14 +337,16 @@ U-Boot source tree, and should be installed outside of the source tree. Users
|
||||||
should set `$PYTHONPATH` prior to running the test script to allow these
|
should set `$PYTHONPATH` prior to running the test script to allow these
|
||||||
modules to be loaded.
|
modules to be loaded.
|
||||||
|
|
||||||
### Board module parameter usage
|
Board module parameter usage
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
The test scripts rely on the following variables being defined by the board
|
The test scripts rely on the following variables being defined by the board
|
||||||
module:
|
module:
|
||||||
|
|
||||||
- None at present.
|
- none at present
|
||||||
|
|
||||||
### U-Boot `.config` feature usage
|
U-Boot `.config` feature usage
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
The test scripts rely on various U-Boot `.config` features, either directly in
|
The test scripts rely on various U-Boot `.config` features, either directly in
|
||||||
order to test those features, or indirectly in order to query information from
|
order to test those features, or indirectly in order to query information from
|
||||||
|
@ -328,7 +363,8 @@ instances of:
|
||||||
- `@pytest.mark.buildconfigspec(...`
|
- `@pytest.mark.buildconfigspec(...`
|
||||||
- `@pytest.mark.notbuildconfigspec(...`
|
- `@pytest.mark.notbuildconfigspec(...`
|
||||||
|
|
||||||
### Complete invocation example
|
Complete invocation example
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
Assuming that you have installed the hook scripts into $HOME/ubtest/bin, and
|
Assuming that you have installed the hook scripts into $HOME/ubtest/bin, and
|
||||||
any required environment configuration Python modules into $HOME/ubtest/py,
|
any required environment configuration Python modules into $HOME/ubtest/py,
|
||||||
|
@ -336,32 +372,33 @@ then you would likely invoke the test script as follows:
|
||||||
|
|
||||||
If U-Boot has already been built:
|
If U-Boot has already been built:
|
||||||
|
|
||||||
```bash
|
.. code-block:: bash
|
||||||
PATH=$HOME/ubtest/bin:$PATH \
|
|
||||||
|
PATH=$HOME/ubtest/bin:$PATH \
|
||||||
PYTHONPATH=${HOME}/ubtest/py/${HOSTNAME}:${PYTHONPATH} \
|
PYTHONPATH=${HOME}/ubtest/py/${HOSTNAME}:${PYTHONPATH} \
|
||||||
./test/py/test.py --bd seaboard
|
./test/py/test.py --bd seaboard
|
||||||
```
|
|
||||||
|
|
||||||
If you want the test script to compile U-Boot for you too, then you likely
|
If you want the test script to compile U-Boot for you too, then you likely
|
||||||
need to set `$CROSS_COMPILE` to allow this, and invoke the test script as
|
need to set `$CROSS_COMPILE` to allow this, and invoke the test script as
|
||||||
follows:
|
follows:
|
||||||
|
|
||||||
```bash
|
.. code-block:: bash
|
||||||
CROSS_COMPILE=arm-none-eabi- \
|
|
||||||
|
CROSS_COMPILE=arm-none-eabi- \
|
||||||
PATH=$HOME/ubtest/bin:$PATH \
|
PATH=$HOME/ubtest/bin:$PATH \
|
||||||
PYTHONPATH=${HOME}/ubtest/py/${HOSTNAME}:${PYTHONPATH} \
|
PYTHONPATH=${HOME}/ubtest/py/${HOSTNAME}:${PYTHONPATH} \
|
||||||
./test/py/test.py --bd seaboard --build
|
./test/py/test.py --bd seaboard --build
|
||||||
```
|
|
||||||
|
|
||||||
or, using buildman to handle it:
|
or, using buildman to handle it:
|
||||||
|
|
||||||
```bash
|
.. code-block:: bash
|
||||||
|
|
||||||
PATH=$HOME/ubtest/bin:$PATH \
|
PATH=$HOME/ubtest/bin:$PATH \
|
||||||
PYTHONPATH=${HOME}/ubtest/py/${HOSTNAME}:${PYTHONPATH} \
|
PYTHONPATH=${HOME}/ubtest/py/${HOSTNAME}:${PYTHONPATH} \
|
||||||
./test/py/test.py --bd seaboard --build --buildman
|
./test/py/test.py --bd seaboard --build --buildman
|
||||||
```
|
|
||||||
|
|
||||||
## Writing tests
|
Writing tests
|
||||||
|
-------------
|
||||||
|
|
||||||
Please refer to the pytest documentation for details of writing pytest tests.
|
Please refer to the pytest documentation for details of writing pytest tests.
|
||||||
Details specific to the U-Boot test suite are described below.
|
Details specific to the U-Boot test suite are described below.
|
290
doc/sphinx/automarkup.py
Normal file
290
doc/sphinx/automarkup.py
Normal file
|
@ -0,0 +1,290 @@
|
||||||
|
# SPDX-License-Identifier: GPL-2.0
|
||||||
|
# Copyright 2019 Jonathan Corbet <corbet@lwn.net>
|
||||||
|
#
|
||||||
|
# Apply kernel-specific tweaks after the initial document processing
|
||||||
|
# has been done.
|
||||||
|
#
|
||||||
|
from docutils import nodes
|
||||||
|
import sphinx
|
||||||
|
from sphinx import addnodes
|
||||||
|
if sphinx.version_info[0] < 2 or \
|
||||||
|
sphinx.version_info[0] == 2 and sphinx.version_info[1] < 1:
|
||||||
|
from sphinx.environment import NoUri
|
||||||
|
else:
|
||||||
|
from sphinx.errors import NoUri
|
||||||
|
import re
|
||||||
|
from itertools import chain
|
||||||
|
|
||||||
|
#
|
||||||
|
# Python 2 lacks re.ASCII...
|
||||||
|
#
|
||||||
|
try:
|
||||||
|
ascii_p3 = re.ASCII
|
||||||
|
except AttributeError:
|
||||||
|
ascii_p3 = 0
|
||||||
|
|
||||||
|
#
|
||||||
|
# Regex nastiness. Of course.
|
||||||
|
# Try to identify "function()" that's not already marked up some
|
||||||
|
# other way. Sphinx doesn't like a lot of stuff right after a
|
||||||
|
# :c:func: block (i.e. ":c:func:`mmap()`s" flakes out), so the last
|
||||||
|
# bit tries to restrict matches to things that won't create trouble.
|
||||||
|
#
|
||||||
|
RE_function = re.compile(r'\b(([a-zA-Z_]\w+)\(\))', flags=ascii_p3)
|
||||||
|
|
||||||
|
#
|
||||||
|
# Sphinx 2 uses the same :c:type role for struct, union, enum and typedef
|
||||||
|
#
|
||||||
|
RE_generic_type = re.compile(r'\b(struct|union|enum|typedef)\s+([a-zA-Z_]\w+)',
|
||||||
|
flags=ascii_p3)
|
||||||
|
|
||||||
|
#
|
||||||
|
# Sphinx 3 uses a different C role for each one of struct, union, enum and
|
||||||
|
# typedef
|
||||||
|
#
|
||||||
|
RE_struct = re.compile(r'\b(struct)\s+([a-zA-Z_]\w+)', flags=ascii_p3)
|
||||||
|
RE_union = re.compile(r'\b(union)\s+([a-zA-Z_]\w+)', flags=ascii_p3)
|
||||||
|
RE_enum = re.compile(r'\b(enum)\s+([a-zA-Z_]\w+)', flags=ascii_p3)
|
||||||
|
RE_typedef = re.compile(r'\b(typedef)\s+([a-zA-Z_]\w+)', flags=ascii_p3)
|
||||||
|
|
||||||
|
#
|
||||||
|
# Detects a reference to a documentation page of the form Documentation/... with
|
||||||
|
# an optional extension
|
||||||
|
#
|
||||||
|
RE_doc = re.compile(r'\bDocumentation(/[\w\-_/]+)(\.\w+)*')
|
||||||
|
|
||||||
|
RE_namespace = re.compile(r'^\s*..\s*c:namespace::\s*(\S+)\s*$')
|
||||||
|
|
||||||
|
#
|
||||||
|
# Reserved C words that we should skip when cross-referencing
|
||||||
|
#
|
||||||
|
Skipnames = [ 'for', 'if', 'register', 'sizeof', 'struct', 'unsigned' ]
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Many places in the docs refer to common system calls. It is
|
||||||
|
# pointless to try to cross-reference them and, as has been known
|
||||||
|
# to happen, somebody defining a function by these names can lead
|
||||||
|
# to the creation of incorrect and confusing cross references. So
|
||||||
|
# just don't even try with these names.
|
||||||
|
#
|
||||||
|
Skipfuncs = [ 'open', 'close', 'read', 'write', 'fcntl', 'mmap',
|
||||||
|
'select', 'poll', 'fork', 'execve', 'clone', 'ioctl',
|
||||||
|
'socket' ]
|
||||||
|
|
||||||
|
c_namespace = ''
|
||||||
|
|
||||||
|
def markup_refs(docname, app, node):
|
||||||
|
t = node.astext()
|
||||||
|
done = 0
|
||||||
|
repl = [ ]
|
||||||
|
#
|
||||||
|
# Associate each regex with the function that will markup its matches
|
||||||
|
#
|
||||||
|
markup_func_sphinx2 = {RE_doc: markup_doc_ref,
|
||||||
|
RE_function: markup_c_ref,
|
||||||
|
RE_generic_type: markup_c_ref}
|
||||||
|
|
||||||
|
markup_func_sphinx3 = {RE_doc: markup_doc_ref,
|
||||||
|
RE_function: markup_func_ref_sphinx3,
|
||||||
|
RE_struct: markup_c_ref,
|
||||||
|
RE_union: markup_c_ref,
|
||||||
|
RE_enum: markup_c_ref,
|
||||||
|
RE_typedef: markup_c_ref}
|
||||||
|
|
||||||
|
if sphinx.version_info[0] >= 3:
|
||||||
|
markup_func = markup_func_sphinx3
|
||||||
|
else:
|
||||||
|
markup_func = markup_func_sphinx2
|
||||||
|
|
||||||
|
match_iterators = [regex.finditer(t) for regex in markup_func]
|
||||||
|
#
|
||||||
|
# Sort all references by the starting position in text
|
||||||
|
#
|
||||||
|
sorted_matches = sorted(chain(*match_iterators), key=lambda m: m.start())
|
||||||
|
for m in sorted_matches:
|
||||||
|
#
|
||||||
|
# Include any text prior to match as a normal text node.
|
||||||
|
#
|
||||||
|
if m.start() > done:
|
||||||
|
repl.append(nodes.Text(t[done:m.start()]))
|
||||||
|
|
||||||
|
#
|
||||||
|
# Call the function associated with the regex that matched this text and
|
||||||
|
# append its return to the text
|
||||||
|
#
|
||||||
|
repl.append(markup_func[m.re](docname, app, m))
|
||||||
|
|
||||||
|
done = m.end()
|
||||||
|
if done < len(t):
|
||||||
|
repl.append(nodes.Text(t[done:]))
|
||||||
|
return repl
|
||||||
|
|
||||||
|
#
|
||||||
|
# In sphinx3 we can cross-reference to C macro and function, each one with its
|
||||||
|
# own C role, but both match the same regex, so we try both.
|
||||||
|
#
|
||||||
|
def markup_func_ref_sphinx3(docname, app, match):
|
||||||
|
class_str = ['c-func', 'c-macro']
|
||||||
|
reftype_str = ['function', 'macro']
|
||||||
|
|
||||||
|
cdom = app.env.domains['c']
|
||||||
|
#
|
||||||
|
# Go through the dance of getting an xref out of the C domain
|
||||||
|
#
|
||||||
|
base_target = match.group(2)
|
||||||
|
target_text = nodes.Text(match.group(0))
|
||||||
|
xref = None
|
||||||
|
possible_targets = [base_target]
|
||||||
|
# Check if this document has a namespace, and if so, try
|
||||||
|
# cross-referencing inside it first.
|
||||||
|
if c_namespace:
|
||||||
|
possible_targets.insert(0, c_namespace + "." + base_target)
|
||||||
|
|
||||||
|
if base_target not in Skipnames:
|
||||||
|
for target in possible_targets:
|
||||||
|
if target not in Skipfuncs:
|
||||||
|
for class_s, reftype_s in zip(class_str, reftype_str):
|
||||||
|
lit_text = nodes.literal(classes=['xref', 'c', class_s])
|
||||||
|
lit_text += target_text
|
||||||
|
pxref = addnodes.pending_xref('', refdomain = 'c',
|
||||||
|
reftype = reftype_s,
|
||||||
|
reftarget = target, modname = None,
|
||||||
|
classname = None)
|
||||||
|
#
|
||||||
|
# XXX The Latex builder will throw NoUri exceptions here,
|
||||||
|
# work around that by ignoring them.
|
||||||
|
#
|
||||||
|
try:
|
||||||
|
xref = cdom.resolve_xref(app.env, docname, app.builder,
|
||||||
|
reftype_s, target, pxref,
|
||||||
|
lit_text)
|
||||||
|
except NoUri:
|
||||||
|
xref = None
|
||||||
|
|
||||||
|
if xref:
|
||||||
|
return xref
|
||||||
|
|
||||||
|
return target_text
|
||||||
|
|
||||||
|
def markup_c_ref(docname, app, match):
|
||||||
|
class_str = {# Sphinx 2 only
|
||||||
|
RE_function: 'c-func',
|
||||||
|
RE_generic_type: 'c-type',
|
||||||
|
# Sphinx 3+ only
|
||||||
|
RE_struct: 'c-struct',
|
||||||
|
RE_union: 'c-union',
|
||||||
|
RE_enum: 'c-enum',
|
||||||
|
RE_typedef: 'c-type',
|
||||||
|
}
|
||||||
|
reftype_str = {# Sphinx 2 only
|
||||||
|
RE_function: 'function',
|
||||||
|
RE_generic_type: 'type',
|
||||||
|
# Sphinx 3+ only
|
||||||
|
RE_struct: 'struct',
|
||||||
|
RE_union: 'union',
|
||||||
|
RE_enum: 'enum',
|
||||||
|
RE_typedef: 'type',
|
||||||
|
}
|
||||||
|
|
||||||
|
cdom = app.env.domains['c']
|
||||||
|
#
|
||||||
|
# Go through the dance of getting an xref out of the C domain
|
||||||
|
#
|
||||||
|
base_target = match.group(2)
|
||||||
|
target_text = nodes.Text(match.group(0))
|
||||||
|
xref = None
|
||||||
|
possible_targets = [base_target]
|
||||||
|
# Check if this document has a namespace, and if so, try
|
||||||
|
# cross-referencing inside it first.
|
||||||
|
if c_namespace:
|
||||||
|
possible_targets.insert(0, c_namespace + "." + base_target)
|
||||||
|
|
||||||
|
if base_target not in Skipnames:
|
||||||
|
for target in possible_targets:
|
||||||
|
if not (match.re == RE_function and target in Skipfuncs):
|
||||||
|
lit_text = nodes.literal(classes=['xref', 'c', class_str[match.re]])
|
||||||
|
lit_text += target_text
|
||||||
|
pxref = addnodes.pending_xref('', refdomain = 'c',
|
||||||
|
reftype = reftype_str[match.re],
|
||||||
|
reftarget = target, modname = None,
|
||||||
|
classname = None)
|
||||||
|
#
|
||||||
|
# XXX The Latex builder will throw NoUri exceptions here,
|
||||||
|
# work around that by ignoring them.
|
||||||
|
#
|
||||||
|
try:
|
||||||
|
xref = cdom.resolve_xref(app.env, docname, app.builder,
|
||||||
|
reftype_str[match.re], target, pxref,
|
||||||
|
lit_text)
|
||||||
|
except NoUri:
|
||||||
|
xref = None
|
||||||
|
|
||||||
|
if xref:
|
||||||
|
return xref
|
||||||
|
|
||||||
|
return target_text
|
||||||
|
|
||||||
|
#
|
||||||
|
# Try to replace a documentation reference of the form Documentation/... with a
|
||||||
|
# cross reference to that page
|
||||||
|
#
|
||||||
|
def markup_doc_ref(docname, app, match):
|
||||||
|
stddom = app.env.domains['std']
|
||||||
|
#
|
||||||
|
# Go through the dance of getting an xref out of the std domain
|
||||||
|
#
|
||||||
|
target = match.group(1)
|
||||||
|
xref = None
|
||||||
|
pxref = addnodes.pending_xref('', refdomain = 'std', reftype = 'doc',
|
||||||
|
reftarget = target, modname = None,
|
||||||
|
classname = None, refexplicit = False)
|
||||||
|
#
|
||||||
|
# XXX The Latex builder will throw NoUri exceptions here,
|
||||||
|
# work around that by ignoring them.
|
||||||
|
#
|
||||||
|
try:
|
||||||
|
xref = stddom.resolve_xref(app.env, docname, app.builder, 'doc',
|
||||||
|
target, pxref, None)
|
||||||
|
except NoUri:
|
||||||
|
xref = None
|
||||||
|
#
|
||||||
|
# Return the xref if we got it; otherwise just return the plain text.
|
||||||
|
#
|
||||||
|
if xref:
|
||||||
|
return xref
|
||||||
|
else:
|
||||||
|
return nodes.Text(match.group(0))
|
||||||
|
|
||||||
|
def get_c_namespace(app, docname):
|
||||||
|
source = app.env.doc2path(docname)
|
||||||
|
with open(source) as f:
|
||||||
|
for l in f:
|
||||||
|
match = RE_namespace.search(l)
|
||||||
|
if match:
|
||||||
|
return match.group(1)
|
||||||
|
return ''
|
||||||
|
|
||||||
|
def auto_markup(app, doctree, name):
|
||||||
|
global c_namespace
|
||||||
|
c_namespace = get_c_namespace(app, name)
|
||||||
|
#
|
||||||
|
# This loop could eventually be improved on. Someday maybe we
|
||||||
|
# want a proper tree traversal with a lot of awareness of which
|
||||||
|
# kinds of nodes to prune. But this works well for now.
|
||||||
|
#
|
||||||
|
# The nodes.literal test catches ``literal text``, its purpose is to
|
||||||
|
# avoid adding cross-references to functions that have been explicitly
|
||||||
|
# marked with cc:func:.
|
||||||
|
#
|
||||||
|
for para in doctree.traverse(nodes.paragraph):
|
||||||
|
for node in para.traverse(nodes.Text):
|
||||||
|
if not isinstance(node.parent, nodes.literal):
|
||||||
|
node.parent.replace(node, markup_refs(name, app, node))
|
||||||
|
|
||||||
|
def setup(app):
|
||||||
|
app.connect('doctree-resolved', auto_markup)
|
||||||
|
return {
|
||||||
|
'parallel_read_safe': True,
|
||||||
|
'parallel_write_safe': True,
|
||||||
|
}
|
|
@ -40,14 +40,94 @@ from sphinx import addnodes
|
||||||
from sphinx.domains.c import c_funcptr_sig_re, c_sig_re
|
from sphinx.domains.c import c_funcptr_sig_re, c_sig_re
|
||||||
from sphinx.domains.c import CObject as Base_CObject
|
from sphinx.domains.c import CObject as Base_CObject
|
||||||
from sphinx.domains.c import CDomain as Base_CDomain
|
from sphinx.domains.c import CDomain as Base_CDomain
|
||||||
|
from itertools import chain
|
||||||
|
import re
|
||||||
|
|
||||||
__version__ = '1.0'
|
__version__ = '1.1'
|
||||||
|
|
||||||
# Get Sphinx version
|
# Get Sphinx version
|
||||||
major, minor, patch = sphinx.version_info[:3]
|
major, minor, patch = sphinx.version_info[:3]
|
||||||
|
|
||||||
|
# Namespace to be prepended to the full name
|
||||||
|
namespace = None
|
||||||
|
|
||||||
|
#
|
||||||
|
# Handle trivial newer c domain tags that are part of Sphinx 3.1 c domain tags
|
||||||
|
# - Store the namespace if ".. c:namespace::" tag is found
|
||||||
|
#
|
||||||
|
RE_namespace = re.compile(r'^\s*..\s*c:namespace::\s*(\S+)\s*$')
|
||||||
|
|
||||||
|
def markup_namespace(match):
|
||||||
|
global namespace
|
||||||
|
|
||||||
|
namespace = match.group(1)
|
||||||
|
|
||||||
|
return ""
|
||||||
|
|
||||||
|
#
|
||||||
|
# Handle c:macro for function-style declaration
|
||||||
|
#
|
||||||
|
RE_macro = re.compile(r'^\s*..\s*c:macro::\s*(\S+)\s+(\S.*)\s*$')
|
||||||
|
def markup_macro(match):
|
||||||
|
return ".. c:function:: " + match.group(1) + ' ' + match.group(2)
|
||||||
|
|
||||||
|
#
|
||||||
|
# Handle newer c domain tags that are evaluated as .. c:type: for
|
||||||
|
# backward-compatibility with Sphinx < 3.0
|
||||||
|
#
|
||||||
|
RE_ctype = re.compile(r'^\s*..\s*c:(struct|union|enum|enumerator|alias)::\s*(.*)$')
|
||||||
|
|
||||||
|
def markup_ctype(match):
|
||||||
|
return ".. c:type:: " + match.group(2)
|
||||||
|
|
||||||
|
#
|
||||||
|
# Handle newer c domain tags that are evaluated as :c:type: for
|
||||||
|
# backward-compatibility with Sphinx < 3.0
|
||||||
|
#
|
||||||
|
RE_ctype_refs = re.compile(r':c:(var|struct|union|enum|enumerator)::`([^\`]+)`')
|
||||||
|
def markup_ctype_refs(match):
|
||||||
|
return ":c:type:`" + match.group(2) + '`'
|
||||||
|
|
||||||
|
#
|
||||||
|
# Simply convert :c:expr: and :c:texpr: into a literal block.
|
||||||
|
#
|
||||||
|
RE_expr = re.compile(r':c:(expr|texpr):`([^\`]+)`')
|
||||||
|
def markup_c_expr(match):
|
||||||
|
return '\ ``' + match.group(2) + '``\ '
|
||||||
|
|
||||||
|
#
|
||||||
|
# Parse Sphinx 3.x C markups, replacing them by backward-compatible ones
|
||||||
|
#
|
||||||
|
def c_markups(app, docname, source):
|
||||||
|
result = ""
|
||||||
|
markup_func = {
|
||||||
|
RE_namespace: markup_namespace,
|
||||||
|
RE_expr: markup_c_expr,
|
||||||
|
RE_macro: markup_macro,
|
||||||
|
RE_ctype: markup_ctype,
|
||||||
|
RE_ctype_refs: markup_ctype_refs,
|
||||||
|
}
|
||||||
|
|
||||||
|
lines = iter(source[0].splitlines(True))
|
||||||
|
for n in lines:
|
||||||
|
match_iterators = [regex.finditer(n) for regex in markup_func]
|
||||||
|
matches = sorted(chain(*match_iterators), key=lambda m: m.start())
|
||||||
|
for m in matches:
|
||||||
|
n = n[:m.start()] + markup_func[m.re](m) + n[m.end():]
|
||||||
|
|
||||||
|
result = result + n
|
||||||
|
|
||||||
|
source[0] = result
|
||||||
|
|
||||||
|
#
|
||||||
|
# Now implements support for the cdomain namespacing logic
|
||||||
|
#
|
||||||
|
|
||||||
def setup(app):
|
def setup(app):
|
||||||
|
|
||||||
|
# Handle easy Sphinx 3.1+ simple new tags: :c:expr and .. c:namespace::
|
||||||
|
app.connect('source-read', c_markups)
|
||||||
|
|
||||||
if (major == 1 and minor < 8):
|
if (major == 1 and minor < 8):
|
||||||
app.override_domain(CDomain)
|
app.override_domain(CDomain)
|
||||||
else:
|
else:
|
||||||
|
@ -75,6 +155,8 @@ class CObject(Base_CObject):
|
||||||
function-like macro, the name of the macro is returned. Otherwise
|
function-like macro, the name of the macro is returned. Otherwise
|
||||||
``False`` is returned. """
|
``False`` is returned. """
|
||||||
|
|
||||||
|
global namespace
|
||||||
|
|
||||||
if not self.objtype == 'function':
|
if not self.objtype == 'function':
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
@ -107,11 +189,16 @@ class CObject(Base_CObject):
|
||||||
param += nodes.emphasis(argname, argname)
|
param += nodes.emphasis(argname, argname)
|
||||||
paramlist += param
|
paramlist += param
|
||||||
|
|
||||||
|
if namespace:
|
||||||
|
fullname = namespace + "." + fullname
|
||||||
|
|
||||||
return fullname
|
return fullname
|
||||||
|
|
||||||
def handle_signature(self, sig, signode):
|
def handle_signature(self, sig, signode):
|
||||||
"""Transform a C signature into RST nodes."""
|
"""Transform a C signature into RST nodes."""
|
||||||
|
|
||||||
|
global namespace
|
||||||
|
|
||||||
fullname = self.handle_func_like_macro(sig, signode)
|
fullname = self.handle_func_like_macro(sig, signode)
|
||||||
if not fullname:
|
if not fullname:
|
||||||
fullname = super(CObject, self).handle_signature(sig, signode)
|
fullname = super(CObject, self).handle_signature(sig, signode)
|
||||||
|
@ -122,6 +209,10 @@ class CObject(Base_CObject):
|
||||||
else:
|
else:
|
||||||
# FIXME: handle :name: value of other declaration types?
|
# FIXME: handle :name: value of other declaration types?
|
||||||
pass
|
pass
|
||||||
|
else:
|
||||||
|
if namespace:
|
||||||
|
fullname = namespace + "." + fullname
|
||||||
|
|
||||||
return fullname
|
return fullname
|
||||||
|
|
||||||
def add_target_and_index(self, name, sig, signode):
|
def add_target_and_index(self, name, sig, signode):
|
||||||
|
|
194
doc/sphinx/kernel_abi.py
Normal file
194
doc/sphinx/kernel_abi.py
Normal file
|
@ -0,0 +1,194 @@
|
||||||
|
# -*- coding: utf-8; mode: python -*-
|
||||||
|
# coding=utf-8
|
||||||
|
# SPDX-License-Identifier: GPL-2.0
|
||||||
|
#
|
||||||
|
u"""
|
||||||
|
kernel-abi
|
||||||
|
~~~~~~~~~~
|
||||||
|
|
||||||
|
Implementation of the ``kernel-abi`` reST-directive.
|
||||||
|
|
||||||
|
:copyright: Copyright (C) 2016 Markus Heiser
|
||||||
|
:copyright: Copyright (C) 2016-2020 Mauro Carvalho Chehab
|
||||||
|
:maintained-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
|
||||||
|
:license: GPL Version 2, June 1991 see Linux/COPYING for details.
|
||||||
|
|
||||||
|
The ``kernel-abi`` (:py:class:`KernelCmd`) directive calls the
|
||||||
|
scripts/get_abi.pl script to parse the Kernel ABI files.
|
||||||
|
|
||||||
|
Overview of directive's argument and options.
|
||||||
|
|
||||||
|
.. code-block:: rst
|
||||||
|
|
||||||
|
.. kernel-abi:: <ABI directory location>
|
||||||
|
:debug:
|
||||||
|
|
||||||
|
The argument ``<ABI directory location>`` is required. It contains the
|
||||||
|
location of the ABI files to be parsed.
|
||||||
|
|
||||||
|
``debug``
|
||||||
|
Inserts a code-block with the *raw* reST. Sometimes it is helpful to see
|
||||||
|
what reST is generated.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
import codecs
|
||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import re
|
||||||
|
import kernellog
|
||||||
|
|
||||||
|
from os import path
|
||||||
|
|
||||||
|
from docutils import nodes, statemachine
|
||||||
|
from docutils.statemachine import ViewList
|
||||||
|
from docutils.parsers.rst import directives, Directive
|
||||||
|
from docutils.utils.error_reporting import ErrorString
|
||||||
|
|
||||||
|
#
|
||||||
|
# AutodocReporter is only good up to Sphinx 1.7
|
||||||
|
#
|
||||||
|
import sphinx
|
||||||
|
|
||||||
|
Use_SSI = sphinx.__version__[:3] >= '1.7'
|
||||||
|
if Use_SSI:
|
||||||
|
from sphinx.util.docutils import switch_source_input
|
||||||
|
else:
|
||||||
|
from sphinx.ext.autodoc import AutodocReporter
|
||||||
|
|
||||||
|
__version__ = '1.0'
|
||||||
|
|
||||||
|
def setup(app):
|
||||||
|
|
||||||
|
app.add_directive("kernel-abi", KernelCmd)
|
||||||
|
return dict(
|
||||||
|
version = __version__
|
||||||
|
, parallel_read_safe = True
|
||||||
|
, parallel_write_safe = True
|
||||||
|
)
|
||||||
|
|
||||||
|
class KernelCmd(Directive):
|
||||||
|
|
||||||
|
u"""KernelABI (``kernel-abi``) directive"""
|
||||||
|
|
||||||
|
required_arguments = 1
|
||||||
|
optional_arguments = 2
|
||||||
|
has_content = False
|
||||||
|
final_argument_whitespace = True
|
||||||
|
|
||||||
|
option_spec = {
|
||||||
|
"debug" : directives.flag,
|
||||||
|
"rst" : directives.unchanged
|
||||||
|
}
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
|
||||||
|
doc = self.state.document
|
||||||
|
if not doc.settings.file_insertion_enabled:
|
||||||
|
raise self.warning("docutils: file insertion disabled")
|
||||||
|
|
||||||
|
env = doc.settings.env
|
||||||
|
cwd = path.dirname(doc.current_source)
|
||||||
|
cmd = "get_abi.pl rest --enable-lineno --dir "
|
||||||
|
cmd += self.arguments[0]
|
||||||
|
|
||||||
|
if 'rst' in self.options:
|
||||||
|
cmd += " --rst-source"
|
||||||
|
|
||||||
|
srctree = path.abspath(os.environ["srctree"])
|
||||||
|
|
||||||
|
fname = cmd
|
||||||
|
|
||||||
|
# extend PATH with $(srctree)/scripts
|
||||||
|
path_env = os.pathsep.join([
|
||||||
|
srctree + os.sep + "scripts",
|
||||||
|
os.environ["PATH"]
|
||||||
|
])
|
||||||
|
shell_env = os.environ.copy()
|
||||||
|
shell_env["PATH"] = path_env
|
||||||
|
shell_env["srctree"] = srctree
|
||||||
|
|
||||||
|
lines = self.runCmd(cmd, shell=True, cwd=cwd, env=shell_env)
|
||||||
|
nodeList = self.nestedParse(lines, self.arguments[0])
|
||||||
|
return nodeList
|
||||||
|
|
||||||
|
def runCmd(self, cmd, **kwargs):
|
||||||
|
u"""Run command ``cmd`` and return it's stdout as unicode."""
|
||||||
|
|
||||||
|
try:
|
||||||
|
proc = subprocess.Popen(
|
||||||
|
cmd
|
||||||
|
, stdout = subprocess.PIPE
|
||||||
|
, stderr = subprocess.PIPE
|
||||||
|
, **kwargs
|
||||||
|
)
|
||||||
|
out, err = proc.communicate()
|
||||||
|
|
||||||
|
out, err = codecs.decode(out, 'utf-8'), codecs.decode(err, 'utf-8')
|
||||||
|
|
||||||
|
if proc.returncode != 0:
|
||||||
|
raise self.severe(
|
||||||
|
u"command '%s' failed with return code %d"
|
||||||
|
% (cmd, proc.returncode)
|
||||||
|
)
|
||||||
|
except OSError as exc:
|
||||||
|
raise self.severe(u"problems with '%s' directive: %s."
|
||||||
|
% (self.name, ErrorString(exc)))
|
||||||
|
return out
|
||||||
|
|
||||||
|
def nestedParse(self, lines, fname):
|
||||||
|
content = ViewList()
|
||||||
|
node = nodes.section()
|
||||||
|
|
||||||
|
if "debug" in self.options:
|
||||||
|
code_block = "\n\n.. code-block:: rst\n :linenos:\n"
|
||||||
|
for l in lines.split("\n"):
|
||||||
|
code_block += "\n " + l
|
||||||
|
lines = code_block + "\n\n"
|
||||||
|
|
||||||
|
line_regex = re.compile("^#define LINENO (\S+)\#([0-9]+)$")
|
||||||
|
ln = 0
|
||||||
|
n = 0
|
||||||
|
f = fname
|
||||||
|
|
||||||
|
for line in lines.split("\n"):
|
||||||
|
n = n + 1
|
||||||
|
match = line_regex.search(line)
|
||||||
|
if match:
|
||||||
|
new_f = match.group(1)
|
||||||
|
|
||||||
|
# Sphinx parser is lazy: it stops parsing contents in the
|
||||||
|
# middle, if it is too big. So, handle it per input file
|
||||||
|
if new_f != f and content:
|
||||||
|
self.do_parse(content, node)
|
||||||
|
content = ViewList()
|
||||||
|
|
||||||
|
f = new_f
|
||||||
|
|
||||||
|
# sphinx counts lines from 0
|
||||||
|
ln = int(match.group(2)) - 1
|
||||||
|
else:
|
||||||
|
content.append(line, f, ln)
|
||||||
|
|
||||||
|
kernellog.info(self.state.document.settings.env.app, "%s: parsed %i lines" % (fname, n))
|
||||||
|
|
||||||
|
if content:
|
||||||
|
self.do_parse(content, node)
|
||||||
|
|
||||||
|
return node.children
|
||||||
|
|
||||||
|
def do_parse(self, content, node):
|
||||||
|
if Use_SSI:
|
||||||
|
with switch_source_input(self.state, content):
|
||||||
|
self.state.nested_parse(content, 0, node, match_titles=1)
|
||||||
|
else:
|
||||||
|
buf = self.state.memo.title_styles, self.state.memo.section_level, self.state.memo.reporter
|
||||||
|
|
||||||
|
self.state.memo.title_styles = []
|
||||||
|
self.state.memo.section_level = 0
|
||||||
|
self.state.memo.reporter = AutodocReporter(content, self.state.memo.reporter)
|
||||||
|
try:
|
||||||
|
self.state.nested_parse(content, 0, node, match_titles=1)
|
||||||
|
finally:
|
||||||
|
self.state.memo.title_styles, self.state.memo.section_level, self.state.memo.reporter = buf
|
169
doc/sphinx/kernel_feat.py
Normal file
169
doc/sphinx/kernel_feat.py
Normal file
|
@ -0,0 +1,169 @@
|
||||||
|
# coding=utf-8
|
||||||
|
# SPDX-License-Identifier: GPL-2.0
|
||||||
|
#
|
||||||
|
u"""
|
||||||
|
kernel-feat
|
||||||
|
~~~~~~~~~~~
|
||||||
|
|
||||||
|
Implementation of the ``kernel-feat`` reST-directive.
|
||||||
|
|
||||||
|
:copyright: Copyright (C) 2016 Markus Heiser
|
||||||
|
:copyright: Copyright (C) 2016-2019 Mauro Carvalho Chehab
|
||||||
|
:maintained-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
|
||||||
|
:license: GPL Version 2, June 1991 see Linux/COPYING for details.
|
||||||
|
|
||||||
|
The ``kernel-feat`` (:py:class:`KernelFeat`) directive calls the
|
||||||
|
scripts/get_feat.pl script to parse the Kernel ABI files.
|
||||||
|
|
||||||
|
Overview of directive's argument and options.
|
||||||
|
|
||||||
|
.. code-block:: rst
|
||||||
|
|
||||||
|
.. kernel-feat:: <ABI directory location>
|
||||||
|
:debug:
|
||||||
|
|
||||||
|
The argument ``<ABI directory location>`` is required. It contains the
|
||||||
|
location of the ABI files to be parsed.
|
||||||
|
|
||||||
|
``debug``
|
||||||
|
Inserts a code-block with the *raw* reST. Sometimes it is helpful to see
|
||||||
|
what reST is generated.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
import codecs
|
||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from os import path
|
||||||
|
|
||||||
|
from docutils import nodes, statemachine
|
||||||
|
from docutils.statemachine import ViewList
|
||||||
|
from docutils.parsers.rst import directives, Directive
|
||||||
|
from docutils.utils.error_reporting import ErrorString
|
||||||
|
|
||||||
|
#
|
||||||
|
# AutodocReporter is only good up to Sphinx 1.7
|
||||||
|
#
|
||||||
|
import sphinx
|
||||||
|
|
||||||
|
Use_SSI = sphinx.__version__[:3] >= '1.7'
|
||||||
|
if Use_SSI:
|
||||||
|
from sphinx.util.docutils import switch_source_input
|
||||||
|
else:
|
||||||
|
from sphinx.ext.autodoc import AutodocReporter
|
||||||
|
|
||||||
|
__version__ = '1.0'
|
||||||
|
|
||||||
|
def setup(app):
|
||||||
|
|
||||||
|
app.add_directive("kernel-feat", KernelFeat)
|
||||||
|
return dict(
|
||||||
|
version = __version__
|
||||||
|
, parallel_read_safe = True
|
||||||
|
, parallel_write_safe = True
|
||||||
|
)
|
||||||
|
|
||||||
|
class KernelFeat(Directive):
|
||||||
|
|
||||||
|
u"""KernelFeat (``kernel-feat``) directive"""
|
||||||
|
|
||||||
|
required_arguments = 1
|
||||||
|
optional_arguments = 2
|
||||||
|
has_content = False
|
||||||
|
final_argument_whitespace = True
|
||||||
|
|
||||||
|
option_spec = {
|
||||||
|
"debug" : directives.flag
|
||||||
|
}
|
||||||
|
|
||||||
|
def warn(self, message, **replace):
|
||||||
|
replace["fname"] = self.state.document.current_source
|
||||||
|
replace["line_no"] = replace.get("line_no", self.lineno)
|
||||||
|
message = ("%(fname)s:%(line_no)s: [kernel-feat WARN] : " + message) % replace
|
||||||
|
self.state.document.settings.env.app.warn(message, prefix="")
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
|
||||||
|
doc = self.state.document
|
||||||
|
if not doc.settings.file_insertion_enabled:
|
||||||
|
raise self.warning("docutils: file insertion disabled")
|
||||||
|
|
||||||
|
env = doc.settings.env
|
||||||
|
cwd = path.dirname(doc.current_source)
|
||||||
|
cmd = "get_feat.pl rest --dir "
|
||||||
|
cmd += self.arguments[0]
|
||||||
|
|
||||||
|
if len(self.arguments) > 1:
|
||||||
|
cmd += " --arch " + self.arguments[1]
|
||||||
|
|
||||||
|
srctree = path.abspath(os.environ["srctree"])
|
||||||
|
|
||||||
|
fname = cmd
|
||||||
|
|
||||||
|
# extend PATH with $(srctree)/scripts
|
||||||
|
path_env = os.pathsep.join([
|
||||||
|
srctree + os.sep + "scripts",
|
||||||
|
os.environ["PATH"]
|
||||||
|
])
|
||||||
|
shell_env = os.environ.copy()
|
||||||
|
shell_env["PATH"] = path_env
|
||||||
|
shell_env["srctree"] = srctree
|
||||||
|
|
||||||
|
lines = self.runCmd(cmd, shell=True, cwd=cwd, env=shell_env)
|
||||||
|
nodeList = self.nestedParse(lines, fname)
|
||||||
|
return nodeList
|
||||||
|
|
||||||
|
def runCmd(self, cmd, **kwargs):
|
||||||
|
u"""Run command ``cmd`` and return it's stdout as unicode."""
|
||||||
|
|
||||||
|
try:
|
||||||
|
proc = subprocess.Popen(
|
||||||
|
cmd
|
||||||
|
, stdout = subprocess.PIPE
|
||||||
|
, stderr = subprocess.PIPE
|
||||||
|
, **kwargs
|
||||||
|
)
|
||||||
|
out, err = proc.communicate()
|
||||||
|
|
||||||
|
out, err = codecs.decode(out, 'utf-8'), codecs.decode(err, 'utf-8')
|
||||||
|
|
||||||
|
if proc.returncode != 0:
|
||||||
|
raise self.severe(
|
||||||
|
u"command '%s' failed with return code %d"
|
||||||
|
% (cmd, proc.returncode)
|
||||||
|
)
|
||||||
|
except OSError as exc:
|
||||||
|
raise self.severe(u"problems with '%s' directive: %s."
|
||||||
|
% (self.name, ErrorString(exc)))
|
||||||
|
return out
|
||||||
|
|
||||||
|
def nestedParse(self, lines, fname):
|
||||||
|
content = ViewList()
|
||||||
|
node = nodes.section()
|
||||||
|
|
||||||
|
if "debug" in self.options:
|
||||||
|
code_block = "\n\n.. code-block:: rst\n :linenos:\n"
|
||||||
|
for l in lines.split("\n"):
|
||||||
|
code_block += "\n " + l
|
||||||
|
lines = code_block + "\n\n"
|
||||||
|
|
||||||
|
for c, l in enumerate(lines.split("\n")):
|
||||||
|
content.append(l, fname, c)
|
||||||
|
|
||||||
|
buf = self.state.memo.title_styles, self.state.memo.section_level, self.state.memo.reporter
|
||||||
|
|
||||||
|
if Use_SSI:
|
||||||
|
with switch_source_input(self.state, content):
|
||||||
|
self.state.nested_parse(content, 0, node, match_titles=1)
|
||||||
|
else:
|
||||||
|
self.state.memo.title_styles = []
|
||||||
|
self.state.memo.section_level = 0
|
||||||
|
self.state.memo.reporter = AutodocReporter(content, self.state.memo.reporter)
|
||||||
|
try:
|
||||||
|
self.state.nested_parse(content, 0, node, match_titles=1)
|
||||||
|
finally:
|
||||||
|
self.state.memo.title_styles, self.state.memo.section_level, self.state.memo.reporter = buf
|
||||||
|
|
||||||
|
return node.children
|
|
@ -62,6 +62,7 @@ class KernelDocDirective(Directive):
|
||||||
'export': directives.unchanged,
|
'export': directives.unchanged,
|
||||||
'internal': directives.unchanged,
|
'internal': directives.unchanged,
|
||||||
'identifiers': directives.unchanged,
|
'identifiers': directives.unchanged,
|
||||||
|
'no-identifiers': directives.unchanged,
|
||||||
'functions': directives.unchanged,
|
'functions': directives.unchanged,
|
||||||
}
|
}
|
||||||
has_content = False
|
has_content = False
|
||||||
|
@ -70,6 +71,11 @@ class KernelDocDirective(Directive):
|
||||||
env = self.state.document.settings.env
|
env = self.state.document.settings.env
|
||||||
cmd = [env.config.kerneldoc_bin, '-rst', '-enable-lineno']
|
cmd = [env.config.kerneldoc_bin, '-rst', '-enable-lineno']
|
||||||
|
|
||||||
|
# Pass the version string to kernel-doc, as it needs to use a different
|
||||||
|
# dialect, depending what the C domain supports for each specific
|
||||||
|
# Sphinx versions
|
||||||
|
cmd += ['-sphinx-version', sphinx.__version__]
|
||||||
|
|
||||||
filename = env.config.kerneldoc_srctree + '/' + self.arguments[0]
|
filename = env.config.kerneldoc_srctree + '/' + self.arguments[0]
|
||||||
export_file_patterns = []
|
export_file_patterns = []
|
||||||
|
|
||||||
|
@ -99,6 +105,12 @@ class KernelDocDirective(Directive):
|
||||||
else:
|
else:
|
||||||
cmd += ['-no-doc-sections']
|
cmd += ['-no-doc-sections']
|
||||||
|
|
||||||
|
if 'no-identifiers' in self.options:
|
||||||
|
no_identifiers = self.options.get('no-identifiers').split()
|
||||||
|
if no_identifiers:
|
||||||
|
for i in no_identifiers:
|
||||||
|
cmd += ['-nosymbol', i]
|
||||||
|
|
||||||
for pattern in export_file_patterns:
|
for pattern in export_file_patterns:
|
||||||
for f in glob.glob(env.config.kerneldoc_srctree + '/' + pattern):
|
for f in glob.glob(env.config.kerneldoc_srctree + '/' + pattern):
|
||||||
env.note_dependency(os.path.abspath(f))
|
env.note_dependency(os.path.abspath(f))
|
||||||
|
@ -136,7 +148,8 @@ class KernelDocDirective(Directive):
|
||||||
lineoffset = int(match.group(1)) - 1
|
lineoffset = int(match.group(1)) - 1
|
||||||
# we must eat our comments since the upset the markup
|
# we must eat our comments since the upset the markup
|
||||||
else:
|
else:
|
||||||
result.append(line, filename, lineoffset)
|
doc = env.srcdir + "/" + env.docname + ":" + str(self.lineno)
|
||||||
|
result.append(line, doc + ": " + filename, lineoffset)
|
||||||
lineoffset += 1
|
lineoffset += 1
|
||||||
|
|
||||||
node = nodes.section()
|
node = nodes.section()
|
||||||
|
|
|
@ -25,4 +25,8 @@ def verbose(app, message):
|
||||||
else:
|
else:
|
||||||
app.verbose(message)
|
app.verbose(message)
|
||||||
|
|
||||||
|
def info(app, message):
|
||||||
|
if UseLogging:
|
||||||
|
logger.info(message)
|
||||||
|
else:
|
||||||
|
app.info(message)
|
||||||
|
|
|
@ -29,7 +29,7 @@ u"""
|
||||||
|
|
||||||
Used tools:
|
Used tools:
|
||||||
|
|
||||||
* ``dot(1)``: Graphviz (http://www.graphviz.org). If Graphviz is not
|
* ``dot(1)``: Graphviz (https://www.graphviz.org). If Graphviz is not
|
||||||
available, the DOT language is inserted as literal-block.
|
available, the DOT language is inserted as literal-block.
|
||||||
|
|
||||||
* SVG to PDF: To generate PDF, you need at least one of this tools:
|
* SVG to PDF: To generate PDF, you need at least one of this tools:
|
||||||
|
@ -41,7 +41,7 @@ u"""
|
||||||
* generate PDF from SVG / used by PDF (LaTeX) builder
|
* generate PDF from SVG / used by PDF (LaTeX) builder
|
||||||
|
|
||||||
* generate SVG (html-builder) and PDF (latex-builder) from DOT files.
|
* generate SVG (html-builder) and PDF (latex-builder) from DOT files.
|
||||||
DOT: see http://www.graphviz.org/content/dot-language
|
DOT: see https://www.graphviz.org/content/dot-language
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@ -182,7 +182,7 @@ def setupTools(app):
|
||||||
kernellog.verbose(app, "use dot(1) from: " + dot_cmd)
|
kernellog.verbose(app, "use dot(1) from: " + dot_cmd)
|
||||||
else:
|
else:
|
||||||
kernellog.warn(app, "dot(1) not found, for better output quality install "
|
kernellog.warn(app, "dot(1) not found, for better output quality install "
|
||||||
"graphviz from http://www.graphviz.org")
|
"graphviz from https://www.graphviz.org")
|
||||||
if convert_cmd:
|
if convert_cmd:
|
||||||
kernellog.verbose(app, "use convert(1) from: " + convert_cmd)
|
kernellog.verbose(app, "use convert(1) from: " + convert_cmd)
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -21,6 +21,29 @@ def loadConfig(namespace):
|
||||||
and os.path.normpath(namespace["__file__"]) != os.path.normpath(config_file) ):
|
and os.path.normpath(namespace["__file__"]) != os.path.normpath(config_file) ):
|
||||||
config_file = os.path.abspath(config_file)
|
config_file = os.path.abspath(config_file)
|
||||||
|
|
||||||
|
# Let's avoid one conf.py file just due to latex_documents
|
||||||
|
start = config_file.find('Documentation/')
|
||||||
|
if start >= 0:
|
||||||
|
start = config_file.find('/', start + 1)
|
||||||
|
|
||||||
|
end = config_file.rfind('/')
|
||||||
|
if start >= 0 and end > 0:
|
||||||
|
dir = config_file[start + 1:end]
|
||||||
|
|
||||||
|
print("source directory: %s" % dir)
|
||||||
|
new_latex_docs = []
|
||||||
|
latex_documents = namespace['latex_documents']
|
||||||
|
|
||||||
|
for l in latex_documents:
|
||||||
|
if l[0].find(dir + '/') == 0:
|
||||||
|
has = True
|
||||||
|
fn = l[0][len(dir) + 1:]
|
||||||
|
new_latex_docs.append((fn, l[1], l[2], l[3], l[4]))
|
||||||
|
break
|
||||||
|
|
||||||
|
namespace['latex_documents'] = new_latex_docs
|
||||||
|
|
||||||
|
# If there is an extra conf.py file, load it
|
||||||
if os.path.isfile(config_file):
|
if os.path.isfile(config_file):
|
||||||
sys.stdout.write("load additional sphinx-config: %s\n" % config_file)
|
sys.stdout.write("load additional sphinx-config: %s\n" % config_file)
|
||||||
config = namespace.copy()
|
config = namespace.copy()
|
||||||
|
@ -29,4 +52,6 @@ def loadConfig(namespace):
|
||||||
del config['__file__']
|
del config['__file__']
|
||||||
namespace.update(config)
|
namespace.update(config)
|
||||||
else:
|
else:
|
||||||
sys.stderr.write("WARNING: additional sphinx-config not found: %s\n" % config_file)
|
config = namespace.copy()
|
||||||
|
config['tags'].add("subproject")
|
||||||
|
namespace.update(config)
|
||||||
|
|
197
doc/sphinx/maintainers_include.py
Executable file
197
doc/sphinx/maintainers_include.py
Executable file
|
@ -0,0 +1,197 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
# SPDX-License-Identifier: GPL-2.0
|
||||||
|
# -*- coding: utf-8; mode: python -*-
|
||||||
|
# pylint: disable=R0903, C0330, R0914, R0912, E0401
|
||||||
|
|
||||||
|
u"""
|
||||||
|
maintainers-include
|
||||||
|
~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Implementation of the ``maintainers-include`` reST-directive.
|
||||||
|
|
||||||
|
:copyright: Copyright (C) 2019 Kees Cook <keescook@chromium.org>
|
||||||
|
:license: GPL Version 2, June 1991 see linux/COPYING for details.
|
||||||
|
|
||||||
|
The ``maintainers-include`` reST-directive performs extensive parsing
|
||||||
|
specific to the Linux kernel's standard "MAINTAINERS" file, in an
|
||||||
|
effort to avoid needing to heavily mark up the original plain text.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import re
|
||||||
|
import os.path
|
||||||
|
|
||||||
|
from docutils import statemachine
|
||||||
|
from docutils.utils.error_reporting import ErrorString
|
||||||
|
from docutils.parsers.rst import Directive
|
||||||
|
from docutils.parsers.rst.directives.misc import Include
|
||||||
|
|
||||||
|
__version__ = '1.0'
|
||||||
|
|
||||||
|
def setup(app):
|
||||||
|
app.add_directive("maintainers-include", MaintainersInclude)
|
||||||
|
return dict(
|
||||||
|
version = __version__,
|
||||||
|
parallel_read_safe = True,
|
||||||
|
parallel_write_safe = True
|
||||||
|
)
|
||||||
|
|
||||||
|
class MaintainersInclude(Include):
|
||||||
|
u"""MaintainersInclude (``maintainers-include``) directive"""
|
||||||
|
required_arguments = 0
|
||||||
|
|
||||||
|
def parse_maintainers(self, path):
|
||||||
|
"""Parse all the MAINTAINERS lines into ReST for human-readability"""
|
||||||
|
|
||||||
|
result = list()
|
||||||
|
result.append(".. _maintainers:")
|
||||||
|
result.append("")
|
||||||
|
|
||||||
|
# Poor man's state machine.
|
||||||
|
descriptions = False
|
||||||
|
maintainers = False
|
||||||
|
subsystems = False
|
||||||
|
|
||||||
|
# Field letter to field name mapping.
|
||||||
|
field_letter = None
|
||||||
|
fields = dict()
|
||||||
|
|
||||||
|
prev = None
|
||||||
|
field_prev = ""
|
||||||
|
field_content = ""
|
||||||
|
|
||||||
|
for line in open(path):
|
||||||
|
if sys.version_info.major == 2:
|
||||||
|
line = unicode(line, 'utf-8')
|
||||||
|
# Have we reached the end of the preformatted Descriptions text?
|
||||||
|
if descriptions and line.startswith('Maintainers'):
|
||||||
|
descriptions = False
|
||||||
|
# Ensure a blank line following the last "|"-prefixed line.
|
||||||
|
result.append("")
|
||||||
|
|
||||||
|
# Start subsystem processing? This is to skip processing the text
|
||||||
|
# between the Maintainers heading and the first subsystem name.
|
||||||
|
if maintainers and not subsystems:
|
||||||
|
if re.search('^[A-Z0-9]', line):
|
||||||
|
subsystems = True
|
||||||
|
|
||||||
|
# Drop needless input whitespace.
|
||||||
|
line = line.rstrip()
|
||||||
|
|
||||||
|
# Linkify all non-wildcard refs to ReST files in Documentation/.
|
||||||
|
pat = '(Documentation/([^\s\?\*]*)\.rst)'
|
||||||
|
m = re.search(pat, line)
|
||||||
|
if m:
|
||||||
|
# maintainers.rst is in a subdirectory, so include "../".
|
||||||
|
line = re.sub(pat, ':doc:`%s <../%s>`' % (m.group(2), m.group(2)), line)
|
||||||
|
|
||||||
|
# Check state machine for output rendering behavior.
|
||||||
|
output = None
|
||||||
|
if descriptions:
|
||||||
|
# Escape the escapes in preformatted text.
|
||||||
|
output = "| %s" % (line.replace("\\", "\\\\"))
|
||||||
|
# Look for and record field letter to field name mappings:
|
||||||
|
# R: Designated *reviewer*: FullName <address@domain>
|
||||||
|
m = re.search("\s(\S):\s", line)
|
||||||
|
if m:
|
||||||
|
field_letter = m.group(1)
|
||||||
|
if field_letter and not field_letter in fields:
|
||||||
|
m = re.search("\*([^\*]+)\*", line)
|
||||||
|
if m:
|
||||||
|
fields[field_letter] = m.group(1)
|
||||||
|
elif subsystems:
|
||||||
|
# Skip empty lines: subsystem parser adds them as needed.
|
||||||
|
if len(line) == 0:
|
||||||
|
continue
|
||||||
|
# Subsystem fields are batched into "field_content"
|
||||||
|
if line[1] != ':':
|
||||||
|
# Render a subsystem entry as:
|
||||||
|
# SUBSYSTEM NAME
|
||||||
|
# ~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
# Flush pending field content.
|
||||||
|
output = field_content + "\n\n"
|
||||||
|
field_content = ""
|
||||||
|
|
||||||
|
# Collapse whitespace in subsystem name.
|
||||||
|
heading = re.sub("\s+", " ", line)
|
||||||
|
output = output + "%s\n%s" % (heading, "~" * len(heading))
|
||||||
|
field_prev = ""
|
||||||
|
else:
|
||||||
|
# Render a subsystem field as:
|
||||||
|
# :Field: entry
|
||||||
|
# entry...
|
||||||
|
field, details = line.split(':', 1)
|
||||||
|
details = details.strip()
|
||||||
|
|
||||||
|
# Mark paths (and regexes) as literal text for improved
|
||||||
|
# readability and to escape any escapes.
|
||||||
|
if field in ['F', 'N', 'X', 'K']:
|
||||||
|
# But only if not already marked :)
|
||||||
|
if not ':doc:' in details:
|
||||||
|
details = '``%s``' % (details)
|
||||||
|
|
||||||
|
# Comma separate email field continuations.
|
||||||
|
if field == field_prev and field_prev in ['M', 'R', 'L']:
|
||||||
|
field_content = field_content + ","
|
||||||
|
|
||||||
|
# Do not repeat field names, so that field entries
|
||||||
|
# will be collapsed together.
|
||||||
|
if field != field_prev:
|
||||||
|
output = field_content + "\n"
|
||||||
|
field_content = ":%s:" % (fields.get(field, field))
|
||||||
|
field_content = field_content + "\n\t%s" % (details)
|
||||||
|
field_prev = field
|
||||||
|
else:
|
||||||
|
output = line
|
||||||
|
|
||||||
|
# Re-split on any added newlines in any above parsing.
|
||||||
|
if output != None:
|
||||||
|
for separated in output.split('\n'):
|
||||||
|
result.append(separated)
|
||||||
|
|
||||||
|
# Update the state machine when we find heading separators.
|
||||||
|
if line.startswith('----------'):
|
||||||
|
if prev.startswith('Descriptions'):
|
||||||
|
descriptions = True
|
||||||
|
if prev.startswith('Maintainers'):
|
||||||
|
maintainers = True
|
||||||
|
|
||||||
|
# Retain previous line for state machine transitions.
|
||||||
|
prev = line
|
||||||
|
|
||||||
|
# Flush pending field contents.
|
||||||
|
if field_content != "":
|
||||||
|
for separated in field_content.split('\n'):
|
||||||
|
result.append(separated)
|
||||||
|
|
||||||
|
output = "\n".join(result)
|
||||||
|
# For debugging the pre-rendered results...
|
||||||
|
#print(output, file=open("/tmp/MAINTAINERS.rst", "w"))
|
||||||
|
|
||||||
|
self.state_machine.insert_input(
|
||||||
|
statemachine.string2lines(output), path)
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
"""Include the MAINTAINERS file as part of this reST file."""
|
||||||
|
if not self.state.document.settings.file_insertion_enabled:
|
||||||
|
raise self.warning('"%s" directive disabled.' % self.name)
|
||||||
|
|
||||||
|
# Walk up source path directories to find Documentation/../
|
||||||
|
path = self.state_machine.document.attributes['source']
|
||||||
|
path = os.path.realpath(path)
|
||||||
|
tail = path
|
||||||
|
while tail != "Documentation" and tail != "":
|
||||||
|
(path, tail) = os.path.split(path)
|
||||||
|
|
||||||
|
# Append "MAINTAINERS"
|
||||||
|
path = os.path.join(path, "MAINTAINERS")
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.state.document.settings.record_dependencies.add(path)
|
||||||
|
lines = self.parse_maintainers(path)
|
||||||
|
except IOError as error:
|
||||||
|
raise self.severe('Problems with "%s" directive path:\n%s.' %
|
||||||
|
(self.name, ErrorString(error)))
|
||||||
|
|
||||||
|
return []
|
33
doc/sphinx/parallel-wrapper.sh
Normal file
33
doc/sphinx/parallel-wrapper.sh
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
#!/bin/sh
|
||||||
|
# SPDX-License-Identifier: GPL-2.0+
|
||||||
|
#
|
||||||
|
# Figure out if we should follow a specific parallelism from the make
|
||||||
|
# environment (as exported by scripts/jobserver-exec), or fall back to
|
||||||
|
# the "auto" parallelism when "-jN" is not specified at the top-level
|
||||||
|
# "make" invocation.
|
||||||
|
|
||||||
|
sphinx="$1"
|
||||||
|
shift || true
|
||||||
|
|
||||||
|
parallel="$PARALLELISM"
|
||||||
|
if [ -z "$parallel" ] ; then
|
||||||
|
# If no parallelism is specified at the top-level make, then
|
||||||
|
# fall back to the expected "-jauto" mode that the "htmldocs"
|
||||||
|
# target has had.
|
||||||
|
auto=$(perl -e 'open IN,"'"$sphinx"' --version 2>&1 |";
|
||||||
|
while (<IN>) {
|
||||||
|
if (m/([\d\.]+)/) {
|
||||||
|
print "auto" if ($1 >= "1.7")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
close IN')
|
||||||
|
if [ -n "$auto" ] ; then
|
||||||
|
parallel="$auto"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
# Only if some parallelism has been determined do we add the -jN option.
|
||||||
|
if [ -n "$parallel" ] ; then
|
||||||
|
parallel="-j$parallel"
|
||||||
|
fi
|
||||||
|
|
||||||
|
exec "$sphinx" $parallel "$@"
|
|
@ -1,4 +1,4 @@
|
||||||
#!/usr/bin/perl
|
#!/usr/bin/env perl
|
||||||
use strict;
|
use strict;
|
||||||
use Text::Tabs;
|
use Text::Tabs;
|
||||||
use Getopt::Long;
|
use Getopt::Long;
|
||||||
|
@ -110,7 +110,7 @@ while (<IN>) {
|
||||||
) {
|
) {
|
||||||
my $s = $1;
|
my $s = $1;
|
||||||
|
|
||||||
$structs{$s} = "struct :c:type:`$s`\\ ";
|
$structs{$s} = "struct $s\\ ";
|
||||||
next;
|
next;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -393,7 +393,7 @@ Report bugs to Mauro Carvalho Chehab <mchehab@kernel.org>
|
||||||
|
|
||||||
Copyright (c) 2016 by Mauro Carvalho Chehab <mchehab+samsung@kernel.org>.
|
Copyright (c) 2016 by Mauro Carvalho Chehab <mchehab+samsung@kernel.org>.
|
||||||
|
|
||||||
License GPLv2: GNU GPL version 2 <http://gnu.org/licenses/gpl.html>.
|
License GPLv2: GNU GPL version 2 <https://gnu.org/licenses/gpl.html>.
|
||||||
|
|
||||||
This is free software: you are free to change and redistribute it.
|
This is free software: you are free to change and redistribute it.
|
||||||
There is NO WARRANTY, to the extent permitted by law.
|
There is NO WARRANTY, to the extent permitted by law.
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
docutils==0.12
|
docutils
|
||||||
Sphinx==1.4.9
|
Sphinx==2.4.4
|
||||||
sphinx_rtd_theme
|
sphinx_rtd_theme
|
||||||
|
six
|
||||||
|
|
|
@ -126,12 +126,10 @@ Root node of the uImage Tree should have the following layout:
|
||||||
load addresses supplied within sub-image nodes. May be omitted when no
|
load addresses supplied within sub-image nodes. May be omitted when no
|
||||||
entry or load addresses are used.
|
entry or load addresses are used.
|
||||||
|
|
||||||
Mandatory node:
|
Mandatory nodes:
|
||||||
- images : This node contains a set of sub-nodes, each of them representing
|
- images : This node contains a set of sub-nodes, each of them representing
|
||||||
single component sub-image (like kernel, ramdisk, etc.). At least one
|
single component sub-image (like kernel, ramdisk, etc.). At least one
|
||||||
sub-image is required.
|
sub-image is required.
|
||||||
|
|
||||||
Optional node:
|
|
||||||
- configurations : Contains a set of available configuration nodes and
|
- configurations : Contains a set of available configuration nodes and
|
||||||
defines a default configuration.
|
defines a default configuration.
|
||||||
|
|
||||||
|
@ -169,8 +167,8 @@ the '/images' node should have the following layout:
|
||||||
to "none".
|
to "none".
|
||||||
|
|
||||||
Conditionally mandatory property:
|
Conditionally mandatory property:
|
||||||
- os : OS name, mandatory for types "kernel" and "ramdisk". Valid OS names
|
- os : OS name, mandatory for types "kernel". Valid OS names are:
|
||||||
are: "openbsd", "netbsd", "freebsd", "4_4bsd", "linux", "svr4", "esix",
|
"openbsd", "netbsd", "freebsd", "4_4bsd", "linux", "svr4", "esix",
|
||||||
"solaris", "irix", "sco", "dell", "ncr", "lynxos", "vxworks", "psos", "qnx",
|
"solaris", "irix", "sco", "dell", "ncr", "lynxos", "vxworks", "psos", "qnx",
|
||||||
"u-boot", "rtems", "unity", "integrity".
|
"u-boot", "rtems", "unity", "integrity".
|
||||||
- arch : Architecture name, mandatory for types: "standalone", "kernel",
|
- arch : Architecture name, mandatory for types: "standalone", "kernel",
|
||||||
|
@ -179,10 +177,13 @@ the '/images' node should have the following layout:
|
||||||
"sparc64", "m68k", "microblaze", "nios2", "blackfin", "avr32", "st200",
|
"sparc64", "m68k", "microblaze", "nios2", "blackfin", "avr32", "st200",
|
||||||
"sandbox".
|
"sandbox".
|
||||||
- entry : entry point address, address size is determined by
|
- entry : entry point address, address size is determined by
|
||||||
'#address-cells' property of the root node. Mandatory for for types:
|
'#address-cells' property of the root node.
|
||||||
"standalone" and "kernel".
|
Mandatory for types: "firmware", and "kernel".
|
||||||
- load : load address, address size is determined by '#address-cells'
|
- load : load address, address size is determined by '#address-cells'
|
||||||
property of the root node. Mandatory for types: "standalone" and "kernel".
|
property of the root node.
|
||||||
|
Mandatory for types: "firmware", and "kernel".
|
||||||
|
- compatible : compatible method for loading image.
|
||||||
|
Mandatory for types: "fpga", and images that do not specify a load address.
|
||||||
|
|
||||||
Optional nodes:
|
Optional nodes:
|
||||||
- hash-1 : Each hash sub-node represents separate hash or checksum
|
- hash-1 : Each hash sub-node represents separate hash or checksum
|
||||||
|
@ -205,9 +206,8 @@ o hash-1
|
||||||
6) '/configurations' node
|
6) '/configurations' node
|
||||||
-------------------------
|
-------------------------
|
||||||
|
|
||||||
The 'configurations' node is optional. If present, it allows to create a
|
The 'configurations' node creates convenient, labeled boot configurations,
|
||||||
convenient, labeled boot configurations, which combine together kernel images
|
which combine together kernel images with their ramdisks and fdt blobs.
|
||||||
with their ramdisks and fdt blobs.
|
|
||||||
|
|
||||||
The 'configurations' node has has the following structure:
|
The 'configurations' node has has the following structure:
|
||||||
|
|
||||||
|
@ -236,27 +236,22 @@ Each configuration has the following structure:
|
||||||
o config-1
|
o config-1
|
||||||
|- description = "configuration description"
|
|- description = "configuration description"
|
||||||
|- kernel = "kernel sub-node unit name"
|
|- kernel = "kernel sub-node unit name"
|
||||||
|- ramdisk = "ramdisk sub-node unit name"
|
|
||||||
|- fdt = "fdt sub-node unit-name" [, "fdt overlay sub-node unit-name", ...]
|
|- fdt = "fdt sub-node unit-name" [, "fdt overlay sub-node unit-name", ...]
|
||||||
|- fpga = "fpga sub-node unit-name"
|
|
||||||
|- loadables = "loadables sub-node unit-name"
|
|- loadables = "loadables sub-node unit-name"
|
||||||
|- compatible = "vendor,board-style device tree compatible string"
|
|- compatible = "vendor,board-style device tree compatible string"
|
||||||
|
|
||||||
|
|
||||||
Mandatory properties:
|
Mandatory properties:
|
||||||
- description : Textual configuration description.
|
- description : Textual configuration description.
|
||||||
- kernel : Unit name of the corresponding kernel image (image sub-node of a
|
- kernel or firmware: Unit name of the corresponding kernel or firmware
|
||||||
"kernel" type).
|
(u-boot, op-tee, etc) image. If both "kernel" and "firmware" are specified,
|
||||||
|
control is passed to the firmware image.
|
||||||
|
|
||||||
Optional properties:
|
Optional properties:
|
||||||
- ramdisk : Unit name of the corresponding ramdisk image (component image
|
|
||||||
node of a "ramdisk" type).
|
|
||||||
- fdt : Unit name of the corresponding fdt blob (component image node of a
|
- fdt : Unit name of the corresponding fdt blob (component image node of a
|
||||||
"fdt type"). Additional fdt overlay nodes can be supplied which signify
|
"fdt type"). Additional fdt overlay nodes can be supplied which signify
|
||||||
that the resulting device tree blob is generated by the first base fdt
|
that the resulting device tree blob is generated by the first base fdt
|
||||||
blob with all subsequent overlays applied.
|
blob with all subsequent overlays applied.
|
||||||
- setup : Unit name of the corresponding setup binary (used for booting
|
|
||||||
an x86 kernel). This contains the setup.bin file built by the kernel.
|
|
||||||
- fpga : Unit name of the corresponding fpga bitstream blob
|
- fpga : Unit name of the corresponding fpga bitstream blob
|
||||||
(component image node of a "fpga type").
|
(component image node of a "fpga type").
|
||||||
- loadables : Unit name containing a list of additional binaries to be
|
- loadables : Unit name containing a list of additional binaries to be
|
||||||
|
|
|
@ -56,6 +56,13 @@ Output format selection (mutually exclusive):
|
||||||
-rst Output reStructuredText format.
|
-rst Output reStructuredText format.
|
||||||
-none Do not output documentation, only warnings.
|
-none Do not output documentation, only warnings.
|
||||||
|
|
||||||
|
Output format selection modifier (affects only ReST output):
|
||||||
|
|
||||||
|
-sphinx-version Use the ReST C domain dialect compatible with an
|
||||||
|
specific Sphinx Version.
|
||||||
|
If not specified, kernel-doc will auto-detect using
|
||||||
|
the sphinx-build version found on PATH.
|
||||||
|
|
||||||
Output selection (mutually exclusive):
|
Output selection (mutually exclusive):
|
||||||
-export Only output documentation for symbols that have been
|
-export Only output documentation for symbols that have been
|
||||||
exported using EXPORT_SYMBOL() or EXPORT_SYMBOL_GPL()
|
exported using EXPORT_SYMBOL() or EXPORT_SYMBOL_GPL()
|
||||||
|
@ -66,9 +73,8 @@ Output selection (mutually exclusive):
|
||||||
-function NAME Only output documentation for the given function(s)
|
-function NAME Only output documentation for the given function(s)
|
||||||
or DOC: section title(s). All other functions and DOC:
|
or DOC: section title(s). All other functions and DOC:
|
||||||
sections are ignored. May be specified multiple times.
|
sections are ignored. May be specified multiple times.
|
||||||
-nofunction NAME Do NOT output documentation for the given function(s);
|
-nosymbol NAME Exclude the specified symbols from the output
|
||||||
only output documentation for the other functions and
|
documentation. May be specified multiple times.
|
||||||
DOC: sections. May be specified multiple times.
|
|
||||||
|
|
||||||
Output selection modifiers:
|
Output selection modifiers:
|
||||||
-no-doc-sections Do not output DOC: sections.
|
-no-doc-sections Do not output DOC: sections.
|
||||||
|
@ -81,6 +87,7 @@ Output selection modifiers:
|
||||||
Other parameters:
|
Other parameters:
|
||||||
-v Verbose output, more warnings and other information.
|
-v Verbose output, more warnings and other information.
|
||||||
-h Print this help.
|
-h Print this help.
|
||||||
|
-Werror Treat warnings as errors.
|
||||||
|
|
||||||
EOF
|
EOF
|
||||||
print $message;
|
print $message;
|
||||||
|
@ -213,7 +220,9 @@ my $type_constant = '\b``([^\`]+)``\b';
|
||||||
my $type_constant2 = '\%([-_\w]+)';
|
my $type_constant2 = '\%([-_\w]+)';
|
||||||
my $type_func = '(\w+)\(\)';
|
my $type_func = '(\w+)\(\)';
|
||||||
my $type_param = '\@(\w*((\.\w+)|(->\w+))*(\.\.\.)?)';
|
my $type_param = '\@(\w*((\.\w+)|(->\w+))*(\.\.\.)?)';
|
||||||
|
my $type_param_ref = '([\!]?)\@(\w*((\.\w+)|(->\w+))*(\.\.\.)?)';
|
||||||
my $type_fp_param = '\@(\w+)\(\)'; # Special RST handling for func ptr params
|
my $type_fp_param = '\@(\w+)\(\)'; # Special RST handling for func ptr params
|
||||||
|
my $type_fp_param2 = '\@(\w+->\S+)\(\)'; # Special RST handling for structs with func ptr params
|
||||||
my $type_env = '(\$\w+)';
|
my $type_env = '(\$\w+)';
|
||||||
my $type_enum = '\&(enum\s*([_\w]+))';
|
my $type_enum = '\&(enum\s*([_\w]+))';
|
||||||
my $type_struct = '\&(struct\s*([_\w]+))';
|
my $type_struct = '\&(struct\s*([_\w]+))';
|
||||||
|
@ -236,6 +245,7 @@ my @highlights_man = (
|
||||||
[$type_typedef, "\\\\fI\$1\\\\fP"],
|
[$type_typedef, "\\\\fI\$1\\\\fP"],
|
||||||
[$type_union, "\\\\fI\$1\\\\fP"],
|
[$type_union, "\\\\fI\$1\\\\fP"],
|
||||||
[$type_param, "\\\\fI\$1\\\\fP"],
|
[$type_param, "\\\\fI\$1\\\\fP"],
|
||||||
|
[$type_param_ref, "\\\\fI\$1\$2\\\\fP"],
|
||||||
[$type_member, "\\\\fI\$1\$2\$3\\\\fP"],
|
[$type_member, "\\\\fI\$1\$2\$3\\\\fP"],
|
||||||
[$type_fallback, "\\\\fI\$1\\\\fP"]
|
[$type_fallback, "\\\\fI\$1\\\\fP"]
|
||||||
);
|
);
|
||||||
|
@ -249,14 +259,15 @@ my @highlights_rst = (
|
||||||
[$type_member_func, "\\:c\\:type\\:`\$1\$2\$3\\\\(\\\\) <\$1>`"],
|
[$type_member_func, "\\:c\\:type\\:`\$1\$2\$3\\\\(\\\\) <\$1>`"],
|
||||||
[$type_member, "\\:c\\:type\\:`\$1\$2\$3 <\$1>`"],
|
[$type_member, "\\:c\\:type\\:`\$1\$2\$3 <\$1>`"],
|
||||||
[$type_fp_param, "**\$1\\\\(\\\\)**"],
|
[$type_fp_param, "**\$1\\\\(\\\\)**"],
|
||||||
[$type_func, "\\:c\\:func\\:`\$1()`"],
|
[$type_fp_param2, "**\$1\\\\(\\\\)**"],
|
||||||
|
[$type_func, "\$1()"],
|
||||||
[$type_enum, "\\:c\\:type\\:`\$1 <\$2>`"],
|
[$type_enum, "\\:c\\:type\\:`\$1 <\$2>`"],
|
||||||
[$type_struct, "\\:c\\:type\\:`\$1 <\$2>`"],
|
[$type_struct, "\\:c\\:type\\:`\$1 <\$2>`"],
|
||||||
[$type_typedef, "\\:c\\:type\\:`\$1 <\$2>`"],
|
[$type_typedef, "\\:c\\:type\\:`\$1 <\$2>`"],
|
||||||
[$type_union, "\\:c\\:type\\:`\$1 <\$2>`"],
|
[$type_union, "\\:c\\:type\\:`\$1 <\$2>`"],
|
||||||
# in rst this can refer to any type
|
# in rst this can refer to any type
|
||||||
[$type_fallback, "\\:c\\:type\\:`\$1`"],
|
[$type_fallback, "\\:c\\:type\\:`\$1`"],
|
||||||
[$type_param, "**\$1**"]
|
[$type_param_ref, "**\$1\$2**"]
|
||||||
);
|
);
|
||||||
my $blankline_rst = "\n";
|
my $blankline_rst = "\n";
|
||||||
|
|
||||||
|
@ -266,9 +277,12 @@ if ($#ARGV == -1) {
|
||||||
}
|
}
|
||||||
|
|
||||||
my $kernelversion;
|
my $kernelversion;
|
||||||
|
my ($sphinx_major, $sphinx_minor, $sphinx_patch);
|
||||||
|
|
||||||
my $dohighlight = "";
|
my $dohighlight = "";
|
||||||
|
|
||||||
my $verbose = 0;
|
my $verbose = 0;
|
||||||
|
my $Werror = 0;
|
||||||
my $output_mode = "rst";
|
my $output_mode = "rst";
|
||||||
my $output_preformatted = 0;
|
my $output_preformatted = 0;
|
||||||
my $no_doc_sections = 0;
|
my $no_doc_sections = 0;
|
||||||
|
@ -280,12 +294,11 @@ my $modulename = "Kernel API";
|
||||||
use constant {
|
use constant {
|
||||||
OUTPUT_ALL => 0, # output all symbols and doc sections
|
OUTPUT_ALL => 0, # output all symbols and doc sections
|
||||||
OUTPUT_INCLUDE => 1, # output only specified symbols
|
OUTPUT_INCLUDE => 1, # output only specified symbols
|
||||||
OUTPUT_EXCLUDE => 2, # output everything except specified symbols
|
OUTPUT_EXPORTED => 2, # output exported symbols
|
||||||
OUTPUT_EXPORTED => 3, # output exported symbols
|
OUTPUT_INTERNAL => 3, # output non-exported symbols
|
||||||
OUTPUT_INTERNAL => 4, # output non-exported symbols
|
|
||||||
};
|
};
|
||||||
my $output_selection = OUTPUT_ALL;
|
my $output_selection = OUTPUT_ALL;
|
||||||
my $show_not_found = 0;
|
my $show_not_found = 0; # No longer used
|
||||||
|
|
||||||
my @export_file_list;
|
my @export_file_list;
|
||||||
|
|
||||||
|
@ -307,6 +320,7 @@ my $man_date = ('January', 'February', 'March', 'April', 'May', 'June',
|
||||||
# CAVEAT EMPTOR! Some of the others I localised may not want to be, which
|
# CAVEAT EMPTOR! Some of the others I localised may not want to be, which
|
||||||
# could cause "use of undefined value" or other bugs.
|
# could cause "use of undefined value" or other bugs.
|
||||||
my ($function, %function_table, %parametertypes, $declaration_purpose);
|
my ($function, %function_table, %parametertypes, $declaration_purpose);
|
||||||
|
my %nosymbol_table = ();
|
||||||
my $declaration_start_line;
|
my $declaration_start_line;
|
||||||
my ($type, $declaration_name, $return_type);
|
my ($type, $declaration_name, $return_type);
|
||||||
my ($newsection, $newcontents, $prototype, $brcount, %source_map);
|
my ($newsection, $newcontents, $prototype, $brcount, %source_map);
|
||||||
|
@ -315,9 +329,21 @@ if (defined($ENV{'KBUILD_VERBOSE'})) {
|
||||||
$verbose = "$ENV{'KBUILD_VERBOSE'}";
|
$verbose = "$ENV{'KBUILD_VERBOSE'}";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (defined($ENV{'KDOC_WERROR'})) {
|
||||||
|
$Werror = "$ENV{'KDOC_WERROR'}";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (defined($ENV{'KCFLAGS'})) {
|
||||||
|
my $kcflags = "$ENV{'KCFLAGS'}";
|
||||||
|
|
||||||
|
if ($kcflags =~ /Werror/) {
|
||||||
|
$Werror = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
# Generated docbook code is inserted in a template at a point where
|
# Generated docbook code is inserted in a template at a point where
|
||||||
# docbook v3.1 requires a non-zero sequence of RefEntry's; see:
|
# docbook v3.1 requires a non-zero sequence of RefEntry's; see:
|
||||||
# http://www.oasis-open.org/docbook/documentation/reference/html/refentry.html
|
# https://www.oasis-open.org/docbook/documentation/reference/html/refentry.html
|
||||||
# We keep track of number of generated entries and generate a dummy
|
# We keep track of number of generated entries and generate a dummy
|
||||||
# if needs be to ensure the expanded template can be postprocessed
|
# if needs be to ensure the expanded template can be postprocessed
|
||||||
# into html.
|
# into html.
|
||||||
|
@ -327,13 +353,14 @@ my $lineprefix="";
|
||||||
|
|
||||||
# Parser states
|
# Parser states
|
||||||
use constant {
|
use constant {
|
||||||
STATE_NORMAL => 0, # normal code
|
STATE_NORMAL => 0, # normal code
|
||||||
STATE_NAME => 1, # looking for function name
|
STATE_NAME => 1, # looking for function name
|
||||||
STATE_BODY_MAYBE => 2, # body - or maybe more description
|
STATE_BODY_MAYBE => 2, # body - or maybe more description
|
||||||
STATE_BODY => 3, # the body of the comment
|
STATE_BODY => 3, # the body of the comment
|
||||||
STATE_PROTO => 4, # scanning prototype
|
STATE_BODY_WITH_BLANK_LINE => 4, # the body, which has a blank line
|
||||||
STATE_DOCBLOCK => 5, # documentation block
|
STATE_PROTO => 5, # scanning prototype
|
||||||
STATE_INLINE => 6, # gathering documentation outside main block
|
STATE_DOCBLOCK => 6, # documentation block
|
||||||
|
STATE_INLINE => 7, # gathering doc outside main block
|
||||||
};
|
};
|
||||||
my $state;
|
my $state;
|
||||||
my $in_doc_sect;
|
my $in_doc_sect;
|
||||||
|
@ -413,10 +440,9 @@ while ($ARGV[0] =~ m/^--?(.*)/) {
|
||||||
$output_selection = OUTPUT_INCLUDE;
|
$output_selection = OUTPUT_INCLUDE;
|
||||||
$function = shift @ARGV;
|
$function = shift @ARGV;
|
||||||
$function_table{$function} = 1;
|
$function_table{$function} = 1;
|
||||||
} elsif ($cmd eq "nofunction") { # output all except specific functions
|
} elsif ($cmd eq "nosymbol") { # Exclude specific symbols
|
||||||
$output_selection = OUTPUT_EXCLUDE;
|
my $symbol = shift @ARGV;
|
||||||
$function = shift @ARGV;
|
$nosymbol_table{$symbol} = 1;
|
||||||
$function_table{$function} = 1;
|
|
||||||
} elsif ($cmd eq "export") { # only exported symbols
|
} elsif ($cmd eq "export") { # only exported symbols
|
||||||
$output_selection = OUTPUT_EXPORTED;
|
$output_selection = OUTPUT_EXPORTED;
|
||||||
%function_table = ();
|
%function_table = ();
|
||||||
|
@ -428,6 +454,8 @@ while ($ARGV[0] =~ m/^--?(.*)/) {
|
||||||
push(@export_file_list, $file);
|
push(@export_file_list, $file);
|
||||||
} elsif ($cmd eq "v") {
|
} elsif ($cmd eq "v") {
|
||||||
$verbose = 1;
|
$verbose = 1;
|
||||||
|
} elsif ($cmd eq "Werror") {
|
||||||
|
$Werror = 1;
|
||||||
} elsif (($cmd eq "h") || ($cmd eq "help")) {
|
} elsif (($cmd eq "h") || ($cmd eq "help")) {
|
||||||
usage();
|
usage();
|
||||||
} elsif ($cmd eq 'no-doc-sections') {
|
} elsif ($cmd eq 'no-doc-sections') {
|
||||||
|
@ -435,7 +463,24 @@ while ($ARGV[0] =~ m/^--?(.*)/) {
|
||||||
} elsif ($cmd eq 'enable-lineno') {
|
} elsif ($cmd eq 'enable-lineno') {
|
||||||
$enable_lineno = 1;
|
$enable_lineno = 1;
|
||||||
} elsif ($cmd eq 'show-not-found') {
|
} elsif ($cmd eq 'show-not-found') {
|
||||||
$show_not_found = 1;
|
$show_not_found = 1; # A no-op but don't fail
|
||||||
|
} elsif ($cmd eq "sphinx-version") {
|
||||||
|
my $ver_string = shift @ARGV;
|
||||||
|
if ($ver_string =~ m/^(\d+)(\.\d+)?(\.\d+)?/) {
|
||||||
|
$sphinx_major = $1;
|
||||||
|
if (defined($2)) {
|
||||||
|
$sphinx_minor = substr($2,1);
|
||||||
|
} else {
|
||||||
|
$sphinx_minor = 0;
|
||||||
|
}
|
||||||
|
if (defined($3)) {
|
||||||
|
$sphinx_patch = substr($3,1)
|
||||||
|
} else {
|
||||||
|
$sphinx_patch = 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
die "Sphinx version should either major.minor or major.minor.patch format\n";
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
# Unknown argument
|
# Unknown argument
|
||||||
usage();
|
usage();
|
||||||
|
@ -444,6 +489,51 @@ while ($ARGV[0] =~ m/^--?(.*)/) {
|
||||||
|
|
||||||
# continue execution near EOF;
|
# continue execution near EOF;
|
||||||
|
|
||||||
|
# The C domain dialect changed on Sphinx 3. So, we need to check the
|
||||||
|
# version in order to produce the right tags.
|
||||||
|
sub findprog($)
|
||||||
|
{
|
||||||
|
foreach(split(/:/, $ENV{PATH})) {
|
||||||
|
return "$_/$_[0]" if(-x "$_/$_[0]");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub get_sphinx_version()
|
||||||
|
{
|
||||||
|
my $ver;
|
||||||
|
|
||||||
|
my $cmd = "sphinx-build";
|
||||||
|
if (!findprog($cmd)) {
|
||||||
|
my $cmd = "sphinx-build3";
|
||||||
|
if (!findprog($cmd)) {
|
||||||
|
$sphinx_major = 1;
|
||||||
|
$sphinx_minor = 2;
|
||||||
|
$sphinx_patch = 0;
|
||||||
|
printf STDERR "Warning: Sphinx version not found. Using default (Sphinx version %d.%d.%d)\n",
|
||||||
|
$sphinx_major, $sphinx_minor, $sphinx_patch;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
open IN, "$cmd --version 2>&1 |";
|
||||||
|
while (<IN>) {
|
||||||
|
if (m/^\s*sphinx-build\s+([\d]+)\.([\d\.]+)(\+\/[\da-f]+)?$/) {
|
||||||
|
$sphinx_major = $1;
|
||||||
|
$sphinx_minor = $2;
|
||||||
|
$sphinx_patch = $3;
|
||||||
|
last;
|
||||||
|
}
|
||||||
|
# Sphinx 1.2.x uses a different format
|
||||||
|
if (m/^\s*Sphinx.*\s+([\d]+)\.([\d\.]+)$/) {
|
||||||
|
$sphinx_major = $1;
|
||||||
|
$sphinx_minor = $2;
|
||||||
|
$sphinx_patch = $3;
|
||||||
|
last;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
close IN;
|
||||||
|
}
|
||||||
|
|
||||||
# get kernel version from env
|
# get kernel version from env
|
||||||
sub get_kernel_version() {
|
sub get_kernel_version() {
|
||||||
my $version = 'unknown kernel version';
|
my $version = 'unknown kernel version';
|
||||||
|
@ -510,11 +600,11 @@ sub dump_doc_section {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return if (defined($nosymbol_table{$name}));
|
||||||
|
|
||||||
if (($output_selection == OUTPUT_ALL) ||
|
if (($output_selection == OUTPUT_ALL) ||
|
||||||
($output_selection == OUTPUT_INCLUDE &&
|
(($output_selection == OUTPUT_INCLUDE) &&
|
||||||
defined($function_table{$name})) ||
|
defined($function_table{$name})))
|
||||||
($output_selection == OUTPUT_EXCLUDE &&
|
|
||||||
!defined($function_table{$name})))
|
|
||||||
{
|
{
|
||||||
dump_section($file, $name, $contents);
|
dump_section($file, $name, $contents);
|
||||||
output_blockhead({'sectionlist' => \@sectionlist,
|
output_blockhead({'sectionlist' => \@sectionlist,
|
||||||
|
@ -597,10 +687,10 @@ sub output_function_man(%) {
|
||||||
$type = $args{'parametertypes'}{$parameter};
|
$type = $args{'parametertypes'}{$parameter};
|
||||||
if ($type =~ m/([^\(]*\(\*)\s*\)\s*\(([^\)]*)\)/) {
|
if ($type =~ m/([^\(]*\(\*)\s*\)\s*\(([^\)]*)\)/) {
|
||||||
# pointer-to-function
|
# pointer-to-function
|
||||||
print ".BI \"" . $parenth . $1 . "\" " . $parameter . " \") (" . $2 . ")" . $post . "\"\n";
|
print ".BI \"" . $parenth . $1 . "\" " . " \") (" . $2 . ")" . $post . "\"\n";
|
||||||
} else {
|
} else {
|
||||||
$type =~ s/([^\*])$/$1 /;
|
$type =~ s/([^\*])$/$1 /;
|
||||||
print ".BI \"" . $parenth . $type . "\" " . $parameter . " \"" . $post . "\"\n";
|
print ".BI \"" . $parenth . $type . "\" " . " \"" . $post . "\"\n";
|
||||||
}
|
}
|
||||||
$count++;
|
$count++;
|
||||||
$parenth = "";
|
$parenth = "";
|
||||||
|
@ -740,6 +830,8 @@ sub output_blockhead_rst(%) {
|
||||||
my ($parameter, $section);
|
my ($parameter, $section);
|
||||||
|
|
||||||
foreach $section (@{$args{'sectionlist'}}) {
|
foreach $section (@{$args{'sectionlist'}}) {
|
||||||
|
next if (defined($nosymbol_table{$section}));
|
||||||
|
|
||||||
if ($output_selection != OUTPUT_INCLUDE) {
|
if ($output_selection != OUTPUT_INCLUDE) {
|
||||||
print "**$section**\n\n";
|
print "**$section**\n\n";
|
||||||
}
|
}
|
||||||
|
@ -825,16 +917,37 @@ sub output_function_rst(%) {
|
||||||
my ($parameter, $section);
|
my ($parameter, $section);
|
||||||
my $oldprefix = $lineprefix;
|
my $oldprefix = $lineprefix;
|
||||||
my $start = "";
|
my $start = "";
|
||||||
|
my $is_macro = 0;
|
||||||
|
|
||||||
if ($args{'typedef'}) {
|
if ($sphinx_major < 3) {
|
||||||
print ".. c:type:: ". $args{'function'} . "\n\n";
|
if ($args{'typedef'}) {
|
||||||
print_lineno($declaration_start_line);
|
print ".. c:type:: ". $args{'function'} . "\n\n";
|
||||||
print " **Typedef**: ";
|
print_lineno($declaration_start_line);
|
||||||
$lineprefix = "";
|
print " **Typedef**: ";
|
||||||
output_highlight_rst($args{'purpose'});
|
$lineprefix = "";
|
||||||
$start = "\n\n**Syntax**\n\n ``";
|
output_highlight_rst($args{'purpose'});
|
||||||
|
$start = "\n\n**Syntax**\n\n ``";
|
||||||
|
$is_macro = 1;
|
||||||
|
} else {
|
||||||
|
print ".. c:function:: ";
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
print ".. c:function:: ";
|
if ($args{'typedef'} || $args{'functiontype'} eq "") {
|
||||||
|
$is_macro = 1;
|
||||||
|
print ".. c:macro:: ". $args{'function'} . "\n\n";
|
||||||
|
} else {
|
||||||
|
print ".. c:function:: ";
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($args{'typedef'}) {
|
||||||
|
print_lineno($declaration_start_line);
|
||||||
|
print " **Typedef**: ";
|
||||||
|
$lineprefix = "";
|
||||||
|
output_highlight_rst($args{'purpose'});
|
||||||
|
$start = "\n\n**Syntax**\n\n ``";
|
||||||
|
} else {
|
||||||
|
print "``" if ($is_macro);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if ($args{'functiontype'} ne "") {
|
if ($args{'functiontype'} ne "") {
|
||||||
$start .= $args{'functiontype'} . " " . $args{'function'} . " (";
|
$start .= $args{'functiontype'} . " " . $args{'function'} . " (";
|
||||||
|
@ -853,15 +966,17 @@ sub output_function_rst(%) {
|
||||||
|
|
||||||
if ($type =~ m/([^\(]*\(\*)\s*\)\s*\(([^\)]*)\)/) {
|
if ($type =~ m/([^\(]*\(\*)\s*\)\s*\(([^\)]*)\)/) {
|
||||||
# pointer-to-function
|
# pointer-to-function
|
||||||
print $1 . $parameter . ") (" . $2;
|
print $1 . $parameter . ") (" . $2 . ")";
|
||||||
} else {
|
} else {
|
||||||
print $type . " " . $parameter;
|
print $type;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ($args{'typedef'}) {
|
if ($is_macro) {
|
||||||
print ");``\n\n";
|
print ")``\n\n";
|
||||||
} else {
|
} else {
|
||||||
print ")\n\n";
|
print ")\n\n";
|
||||||
|
}
|
||||||
|
if (!$args{'typedef'}) {
|
||||||
print_lineno($declaration_start_line);
|
print_lineno($declaration_start_line);
|
||||||
$lineprefix = " ";
|
$lineprefix = " ";
|
||||||
output_highlight_rst($args{'purpose'});
|
output_highlight_rst($args{'purpose'});
|
||||||
|
@ -876,7 +991,7 @@ sub output_function_rst(%) {
|
||||||
$type = $args{'parametertypes'}{$parameter};
|
$type = $args{'parametertypes'}{$parameter};
|
||||||
|
|
||||||
if ($type ne "") {
|
if ($type ne "") {
|
||||||
print "``$type $parameter``\n";
|
print "``$type``\n";
|
||||||
} else {
|
} else {
|
||||||
print "``$parameter``\n";
|
print "``$parameter``\n";
|
||||||
}
|
}
|
||||||
|
@ -917,9 +1032,14 @@ sub output_enum_rst(%) {
|
||||||
my ($parameter);
|
my ($parameter);
|
||||||
my $oldprefix = $lineprefix;
|
my $oldprefix = $lineprefix;
|
||||||
my $count;
|
my $count;
|
||||||
my $name = "enum " . $args{'enum'};
|
|
||||||
|
|
||||||
print "\n\n.. c:type:: " . $name . "\n\n";
|
if ($sphinx_major < 3) {
|
||||||
|
my $name = "enum " . $args{'enum'};
|
||||||
|
print "\n\n.. c:type:: " . $name . "\n\n";
|
||||||
|
} else {
|
||||||
|
my $name = $args{'enum'};
|
||||||
|
print "\n\n.. c:enum:: " . $name . "\n\n";
|
||||||
|
}
|
||||||
print_lineno($declaration_start_line);
|
print_lineno($declaration_start_line);
|
||||||
$lineprefix = " ";
|
$lineprefix = " ";
|
||||||
output_highlight_rst($args{'purpose'});
|
output_highlight_rst($args{'purpose'});
|
||||||
|
@ -945,8 +1065,13 @@ sub output_typedef_rst(%) {
|
||||||
my %args = %{$_[0]};
|
my %args = %{$_[0]};
|
||||||
my ($parameter);
|
my ($parameter);
|
||||||
my $oldprefix = $lineprefix;
|
my $oldprefix = $lineprefix;
|
||||||
my $name = "typedef " . $args{'typedef'};
|
my $name;
|
||||||
|
|
||||||
|
if ($sphinx_major < 3) {
|
||||||
|
$name = "typedef " . $args{'typedef'};
|
||||||
|
} else {
|
||||||
|
$name = $args{'typedef'};
|
||||||
|
}
|
||||||
print "\n\n.. c:type:: " . $name . "\n\n";
|
print "\n\n.. c:type:: " . $name . "\n\n";
|
||||||
print_lineno($declaration_start_line);
|
print_lineno($declaration_start_line);
|
||||||
$lineprefix = " ";
|
$lineprefix = " ";
|
||||||
|
@ -961,9 +1086,18 @@ sub output_struct_rst(%) {
|
||||||
my %args = %{$_[0]};
|
my %args = %{$_[0]};
|
||||||
my ($parameter);
|
my ($parameter);
|
||||||
my $oldprefix = $lineprefix;
|
my $oldprefix = $lineprefix;
|
||||||
my $name = $args{'type'} . " " . $args{'struct'};
|
|
||||||
|
|
||||||
print "\n\n.. c:type:: " . $name . "\n\n";
|
if ($sphinx_major < 3) {
|
||||||
|
my $name = $args{'type'} . " " . $args{'struct'};
|
||||||
|
print "\n\n.. c:type:: " . $name . "\n\n";
|
||||||
|
} else {
|
||||||
|
my $name = $args{'struct'};
|
||||||
|
if ($args{'type'} eq 'union') {
|
||||||
|
print "\n\n.. c:union:: " . $name . "\n\n";
|
||||||
|
} else {
|
||||||
|
print "\n\n.. c:struct:: " . $name . "\n\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
print_lineno($declaration_start_line);
|
print_lineno($declaration_start_line);
|
||||||
$lineprefix = " ";
|
$lineprefix = " ";
|
||||||
output_highlight_rst($args{'purpose'});
|
output_highlight_rst($args{'purpose'});
|
||||||
|
@ -1022,12 +1156,14 @@ sub output_declaration {
|
||||||
my $name = shift;
|
my $name = shift;
|
||||||
my $functype = shift;
|
my $functype = shift;
|
||||||
my $func = "output_${functype}_$output_mode";
|
my $func = "output_${functype}_$output_mode";
|
||||||
|
|
||||||
|
return if (defined($nosymbol_table{$name}));
|
||||||
|
|
||||||
if (($output_selection == OUTPUT_ALL) ||
|
if (($output_selection == OUTPUT_ALL) ||
|
||||||
(($output_selection == OUTPUT_INCLUDE ||
|
(($output_selection == OUTPUT_INCLUDE ||
|
||||||
$output_selection == OUTPUT_EXPORTED) &&
|
$output_selection == OUTPUT_EXPORTED) &&
|
||||||
defined($function_table{$name})) ||
|
defined($function_table{$name})) ||
|
||||||
(($output_selection == OUTPUT_EXCLUDE ||
|
($output_selection == OUTPUT_INTERNAL &&
|
||||||
$output_selection == OUTPUT_INTERNAL) &&
|
|
||||||
!($functype eq "function" && defined($function_table{$name}))))
|
!($functype eq "function" && defined($function_table{$name}))))
|
||||||
{
|
{
|
||||||
&$func(@_);
|
&$func(@_);
|
||||||
|
@ -1062,7 +1198,7 @@ sub dump_struct($$) {
|
||||||
my $x = shift;
|
my $x = shift;
|
||||||
my $file = shift;
|
my $file = shift;
|
||||||
|
|
||||||
if ($x =~ /(struct|union)\s+(\w+)\s*\{(.*)\}(\s*(__packed|__aligned|__attribute__\s*\(\([a-z0-9,_\s\(\)]*\)\)))*/) {
|
if ($x =~ /(struct|union)\s+(\w+)\s*\{(.*)\}(\s*(__packed|__aligned|____cacheline_aligned_in_smp|____cacheline_aligned|__attribute__\s*\(\([a-z0-9,_\s\(\)]*\)\)))*/) {
|
||||||
my $decl_type = $1;
|
my $decl_type = $1;
|
||||||
$declaration_name = $2;
|
$declaration_name = $2;
|
||||||
my $members = $3;
|
my $members = $3;
|
||||||
|
@ -1073,11 +1209,15 @@ sub dump_struct($$) {
|
||||||
# strip comments:
|
# strip comments:
|
||||||
$members =~ s/\/\*.*?\*\///gos;
|
$members =~ s/\/\*.*?\*\///gos;
|
||||||
# strip attributes
|
# strip attributes
|
||||||
$members =~ s/\s*__attribute__\s*\(\([a-z0-9,_\*\s\(\)]*\)\)//gi;
|
$members =~ s/\s*__attribute__\s*\(\([a-z0-9,_\*\s\(\)]*\)\)/ /gi;
|
||||||
$members =~ s/\s*__aligned\s*\([^;]*\)//gos;
|
$members =~ s/\s*__aligned\s*\([^;]*\)/ /gos;
|
||||||
$members =~ s/\s*__packed\s*//gos;
|
$members =~ s/\s*__packed\s*/ /gos;
|
||||||
$members =~ s/\s*CRYPTO_MINALIGN_ATTR//gos;
|
$members =~ s/\s*CRYPTO_MINALIGN_ATTR/ /gos;
|
||||||
|
$members =~ s/\s*____cacheline_aligned_in_smp/ /gos;
|
||||||
|
$members =~ s/\s*____cacheline_aligned/ /gos;
|
||||||
|
|
||||||
# replace DECLARE_BITMAP
|
# replace DECLARE_BITMAP
|
||||||
|
$members =~ s/__ETHTOOL_DECLARE_LINK_MODE_MASK\s*\(([^\)]+)\)/DECLARE_BITMAP($1, __ETHTOOL_LINK_MODE_MASK_NBITS)/gos;
|
||||||
$members =~ s/DECLARE_BITMAP\s*\(([^,)]+),\s*([^,)]+)\)/unsigned long $1\[BITS_TO_LONGS($2)\]/gos;
|
$members =~ s/DECLARE_BITMAP\s*\(([^,)]+),\s*([^,)]+)\)/unsigned long $1\[BITS_TO_LONGS($2)\]/gos;
|
||||||
# replace DECLARE_HASHTABLE
|
# replace DECLARE_HASHTABLE
|
||||||
$members =~ s/DECLARE_HASHTABLE\s*\(([^,)]+),\s*([^,)]+)\)/unsigned long $1\[1 << (($2) - 1)\]/gos;
|
$members =~ s/DECLARE_HASHTABLE\s*\(([^,)]+),\s*([^,)]+)\)/unsigned long $1\[1 << (($2) - 1)\]/gos;
|
||||||
|
@ -1204,6 +1344,8 @@ sub show_warnings($$) {
|
||||||
my $functype = shift;
|
my $functype = shift;
|
||||||
my $name = shift;
|
my $name = shift;
|
||||||
|
|
||||||
|
return 0 if (defined($nosymbol_table{$name}));
|
||||||
|
|
||||||
return 1 if ($output_selection == OUTPUT_ALL);
|
return 1 if ($output_selection == OUTPUT_ALL);
|
||||||
|
|
||||||
if ($output_selection == OUTPUT_EXPORTED) {
|
if ($output_selection == OUTPUT_EXPORTED) {
|
||||||
|
@ -1227,27 +1369,28 @@ sub show_warnings($$) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ($output_selection == OUTPUT_EXCLUDE) {
|
|
||||||
if (!defined($function_table{$name})) {
|
|
||||||
return 1;
|
|
||||||
} else {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
die("Please add the new output type at show_warnings()");
|
die("Please add the new output type at show_warnings()");
|
||||||
}
|
}
|
||||||
|
|
||||||
sub dump_enum($$) {
|
sub dump_enum($$) {
|
||||||
my $x = shift;
|
my $x = shift;
|
||||||
my $file = shift;
|
my $file = shift;
|
||||||
|
my $members;
|
||||||
|
|
||||||
|
|
||||||
$x =~ s@/\*.*?\*/@@gos; # strip comments.
|
$x =~ s@/\*.*?\*/@@gos; # strip comments.
|
||||||
# strip #define macros inside enums
|
# strip #define macros inside enums
|
||||||
$x =~ s@#\s*((define|ifdef)\s+|endif)[^;]*;@@gos;
|
$x =~ s@#\s*((define|ifdef)\s+|endif)[^;]*;@@gos;
|
||||||
|
|
||||||
if ($x =~ /enum\s+(\w+)\s*\{(.*)\}/) {
|
if ($x =~ /typedef\s+enum\s*\{(.*)\}\s*(\w*)\s*;/) {
|
||||||
|
$declaration_name = $2;
|
||||||
|
$members = $1;
|
||||||
|
} elsif ($x =~ /enum\s+(\w*)\s*\{(.*)\}/) {
|
||||||
$declaration_name = $1;
|
$declaration_name = $1;
|
||||||
my $members = $2;
|
$members = $2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($members) {
|
||||||
my %_members;
|
my %_members;
|
||||||
|
|
||||||
$members =~ s/\s+$//;
|
$members =~ s/\s+$//;
|
||||||
|
@ -1282,27 +1425,31 @@ sub dump_enum($$) {
|
||||||
'sections' => \%sections,
|
'sections' => \%sections,
|
||||||
'purpose' => $declaration_purpose
|
'purpose' => $declaration_purpose
|
||||||
});
|
});
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
print STDERR "${file}:$.: error: Cannot parse enum!\n";
|
print STDERR "${file}:$.: error: Cannot parse enum!\n";
|
||||||
++$errors;
|
++$errors;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
my $typedef_type = qr { ((?:\s+[\w\*]+\b){1,8})\s* }x;
|
||||||
|
my $typedef_ident = qr { \*?\s*(\w\S+)\s* }x;
|
||||||
|
my $typedef_args = qr { \s*\((.*)\); }x;
|
||||||
|
|
||||||
|
my $typedef1 = qr { typedef$typedef_type\($typedef_ident\)$typedef_args }x;
|
||||||
|
my $typedef2 = qr { typedef$typedef_type$typedef_ident$typedef_args }x;
|
||||||
|
|
||||||
sub dump_typedef($$) {
|
sub dump_typedef($$) {
|
||||||
my $x = shift;
|
my $x = shift;
|
||||||
my $file = shift;
|
my $file = shift;
|
||||||
|
|
||||||
$x =~ s@/\*.*?\*/@@gos; # strip comments.
|
$x =~ s@/\*.*?\*/@@gos; # strip comments.
|
||||||
|
|
||||||
# Parse function prototypes
|
# Parse function typedef prototypes
|
||||||
if ($x =~ /typedef\s+(\w+)\s*\(\*\s*(\w\S+)\s*\)\s*\((.*)\);/ ||
|
if ($x =~ $typedef1 || $x =~ $typedef2) {
|
||||||
$x =~ /typedef\s+(\w+)\s*(\w\S+)\s*\s*\((.*)\);/) {
|
|
||||||
|
|
||||||
# Function typedefs
|
|
||||||
$return_type = $1;
|
$return_type = $1;
|
||||||
$declaration_name = $2;
|
$declaration_name = $2;
|
||||||
my $args = $3;
|
my $args = $3;
|
||||||
|
$return_type =~ s/^\s+//;
|
||||||
|
|
||||||
create_parameterlist($args, ',', $file, $declaration_name);
|
create_parameterlist($args, ',', $file, $declaration_name);
|
||||||
|
|
||||||
|
@ -1369,25 +1516,27 @@ sub create_parameterlist($$$$) {
|
||||||
foreach my $arg (split($splitter, $args)) {
|
foreach my $arg (split($splitter, $args)) {
|
||||||
# strip comments
|
# strip comments
|
||||||
$arg =~ s/\/\*.*\*\///;
|
$arg =~ s/\/\*.*\*\///;
|
||||||
|
$arg =~ s/\s*EFIAPI\s*/ /g;
|
||||||
# strip leading/trailing spaces
|
# strip leading/trailing spaces
|
||||||
$arg =~ s/^\s*//;
|
$arg =~ s/^\s*//;
|
||||||
$arg =~ s/\s*$//;
|
$arg =~ s/\s*$//;
|
||||||
$arg =~ s/\s+/ /;
|
$arg =~ s/\s+/ /;
|
||||||
|
|
||||||
|
|
||||||
if ($arg =~ /^#/) {
|
if ($arg =~ /^#/) {
|
||||||
# Treat preprocessor directive as a typeless variable just to fill
|
# Treat preprocessor directive as a typeless variable just to fill
|
||||||
# corresponding data structures "correctly". Catch it later in
|
# corresponding data structures "correctly". Catch it later in
|
||||||
# output_* subs.
|
# output_* subs.
|
||||||
push_parameter($arg, "", $file);
|
push_parameter($arg, "", "", $file);
|
||||||
} elsif ($arg =~ m/\(.+\)\s*\(/) {
|
} elsif ($arg =~ m/\(.+\)\s*\(/) {
|
||||||
# pointer-to-function
|
# pointer-to-function
|
||||||
$arg =~ tr/#/,/;
|
$arg =~ tr/#/,/;
|
||||||
$arg =~ m/[^\(]+\([\w\s]*\*?\s*([\w\.]*)\s*\)/;
|
$arg =~ m/[^\(]+\(\s*\*?\s*([\w\.]*)\s*\)/;
|
||||||
$param = $1;
|
$param = $1;
|
||||||
$type = $arg;
|
$type = $arg;
|
||||||
$type =~ s/([^\(]+\(\*?)\s*$param/$1/;
|
$type =~ s/([^\(]+\(\*?)\s*$param/$1/;
|
||||||
save_struct_actual($param);
|
save_struct_actual($param);
|
||||||
push_parameter($param, $type, $file, $declaration_name);
|
push_parameter($param, $type, $arg, $file, $declaration_name);
|
||||||
} elsif ($arg) {
|
} elsif ($arg) {
|
||||||
$arg =~ s/\s*:\s*/:/g;
|
$arg =~ s/\s*:\s*/:/g;
|
||||||
$arg =~ s/\s*\[/\[/g;
|
$arg =~ s/\s*\[/\[/g;
|
||||||
|
@ -1412,26 +1561,28 @@ sub create_parameterlist($$$$) {
|
||||||
foreach $param (@args) {
|
foreach $param (@args) {
|
||||||
if ($param =~ m/^(\*+)\s*(.*)/) {
|
if ($param =~ m/^(\*+)\s*(.*)/) {
|
||||||
save_struct_actual($2);
|
save_struct_actual($2);
|
||||||
push_parameter($2, "$type $1", $file, $declaration_name);
|
|
||||||
|
push_parameter($2, "$type $1", $arg, $file, $declaration_name);
|
||||||
}
|
}
|
||||||
elsif ($param =~ m/(.*?):(\d+)/) {
|
elsif ($param =~ m/(.*?):(\d+)/) {
|
||||||
if ($type ne "") { # skip unnamed bit-fields
|
if ($type ne "") { # skip unnamed bit-fields
|
||||||
save_struct_actual($1);
|
save_struct_actual($1);
|
||||||
push_parameter($1, "$type:$2", $file, $declaration_name)
|
push_parameter($1, "$type:$2", $arg, $file, $declaration_name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
save_struct_actual($param);
|
save_struct_actual($param);
|
||||||
push_parameter($param, $type, $file, $declaration_name);
|
push_parameter($param, $type, $arg, $file, $declaration_name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sub push_parameter($$$$) {
|
sub push_parameter($$$$$) {
|
||||||
my $param = shift;
|
my $param = shift;
|
||||||
my $type = shift;
|
my $type = shift;
|
||||||
|
my $org_arg = shift;
|
||||||
my $file = shift;
|
my $file = shift;
|
||||||
my $declaration_name = shift;
|
my $declaration_name = shift;
|
||||||
|
|
||||||
|
@ -1449,6 +1600,10 @@ sub push_parameter($$$$) {
|
||||||
# handles unnamed variable parameters
|
# handles unnamed variable parameters
|
||||||
$param = "...";
|
$param = "...";
|
||||||
}
|
}
|
||||||
|
elsif ($param =~ /\w\.\.\.$/) {
|
||||||
|
# for named variable parameters of the form `x...`, remove the dots
|
||||||
|
$param =~ s/\.\.\.$//;
|
||||||
|
}
|
||||||
if (!defined $parameterdescs{$param} || $parameterdescs{$param} eq "") {
|
if (!defined $parameterdescs{$param} || $parameterdescs{$param} eq "") {
|
||||||
$parameterdescs{$param} = "variable arguments";
|
$parameterdescs{$param} = "variable arguments";
|
||||||
}
|
}
|
||||||
|
@ -1491,8 +1646,8 @@ sub push_parameter($$$$) {
|
||||||
# "[blah" in a parameter string;
|
# "[blah" in a parameter string;
|
||||||
###$param =~ s/\s*//g;
|
###$param =~ s/\s*//g;
|
||||||
push @parameterlist, $param;
|
push @parameterlist, $param;
|
||||||
$type =~ s/\s\s+/ /g;
|
$org_arg =~ s/\s\s+/ /g;
|
||||||
$parametertypes{$param} = $type;
|
$parametertypes{$param} = $org_arg;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub check_sections($$$$$) {
|
sub check_sections($$$$$) {
|
||||||
|
@ -1566,6 +1721,8 @@ sub dump_function($$) {
|
||||||
my $file = shift;
|
my $file = shift;
|
||||||
my $noret = 0;
|
my $noret = 0;
|
||||||
|
|
||||||
|
print_lineno($new_start_line);
|
||||||
|
|
||||||
$prototype =~ s/^static +//;
|
$prototype =~ s/^static +//;
|
||||||
$prototype =~ s/^extern +//;
|
$prototype =~ s/^extern +//;
|
||||||
$prototype =~ s/^asmlinkage +//;
|
$prototype =~ s/^asmlinkage +//;
|
||||||
|
@ -1580,6 +1737,7 @@ sub dump_function($$) {
|
||||||
$prototype =~ s/__must_check +//;
|
$prototype =~ s/__must_check +//;
|
||||||
$prototype =~ s/__weak +//;
|
$prototype =~ s/__weak +//;
|
||||||
$prototype =~ s/__sched +//;
|
$prototype =~ s/__sched +//;
|
||||||
|
$prototype =~ s/__printf\s*\(\s*\d*\s*,\s*\d*\s*\) +//;
|
||||||
my $define = $prototype =~ s/^#\s*define\s+//; #ak added
|
my $define = $prototype =~ s/^#\s*define\s+//; #ak added
|
||||||
$prototype =~ s/__attribute__\s*\(\(
|
$prototype =~ s/__attribute__\s*\(\(
|
||||||
(?:
|
(?:
|
||||||
|
@ -1640,30 +1798,48 @@ sub dump_function($$) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
my $prms = join " ", @parameterlist;
|
my $prms = join " ", @parameterlist;
|
||||||
check_sections($file, $declaration_name, "function", $sectcheck, $prms);
|
check_sections($file, $declaration_name, "function", $sectcheck, $prms);
|
||||||
|
|
||||||
# This check emits a lot of warnings at the moment, because many
|
# This check emits a lot of warnings at the moment, because many
|
||||||
# functions don't have a 'Return' doc section. So until the number
|
# functions don't have a 'Return' doc section. So until the number
|
||||||
# of warnings goes sufficiently down, the check is only performed in
|
# of warnings goes sufficiently down, the check is only performed in
|
||||||
# verbose mode.
|
# verbose mode.
|
||||||
# TODO: always perform the check.
|
# TODO: always perform the check.
|
||||||
if ($verbose && !$noret) {
|
if ($verbose && !$noret) {
|
||||||
check_return_section($file, $declaration_name, $return_type);
|
check_return_section($file, $declaration_name, $return_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
output_declaration($declaration_name,
|
# The function parser can be called with a typedef parameter.
|
||||||
'function',
|
# Handle it.
|
||||||
{'function' => $declaration_name,
|
if ($return_type =~ /typedef/) {
|
||||||
'module' => $modulename,
|
output_declaration($declaration_name,
|
||||||
'functiontype' => $return_type,
|
'function',
|
||||||
'parameterlist' => \@parameterlist,
|
{'function' => $declaration_name,
|
||||||
'parameterdescs' => \%parameterdescs,
|
'typedef' => 1,
|
||||||
'parametertypes' => \%parametertypes,
|
'module' => $modulename,
|
||||||
'sectionlist' => \@sectionlist,
|
'functiontype' => $return_type,
|
||||||
'sections' => \%sections,
|
'parameterlist' => \@parameterlist,
|
||||||
'purpose' => $declaration_purpose
|
'parameterdescs' => \%parameterdescs,
|
||||||
});
|
'parametertypes' => \%parametertypes,
|
||||||
|
'sectionlist' => \@sectionlist,
|
||||||
|
'sections' => \%sections,
|
||||||
|
'purpose' => $declaration_purpose
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
output_declaration($declaration_name,
|
||||||
|
'function',
|
||||||
|
{'function' => $declaration_name,
|
||||||
|
'module' => $modulename,
|
||||||
|
'functiontype' => $return_type,
|
||||||
|
'parameterlist' => \@parameterlist,
|
||||||
|
'parameterdescs' => \%parameterdescs,
|
||||||
|
'parametertypes' => \%parametertypes,
|
||||||
|
'sectionlist' => \@sectionlist,
|
||||||
|
'sections' => \%sections,
|
||||||
|
'purpose' => $declaration_purpose
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sub reset_state {
|
sub reset_state {
|
||||||
|
@ -1758,6 +1934,11 @@ sub process_proto_function($$) {
|
||||||
$prototype =~ s@/\*.*?\*/@@gos; # strip comments.
|
$prototype =~ s@/\*.*?\*/@@gos; # strip comments.
|
||||||
$prototype =~ s@[\r\n]+@ @gos; # strip newlines/cr's.
|
$prototype =~ s@[\r\n]+@ @gos; # strip newlines/cr's.
|
||||||
$prototype =~ s@^\s+@@gos; # strip leading spaces
|
$prototype =~ s@^\s+@@gos; # strip leading spaces
|
||||||
|
|
||||||
|
# Handle prototypes for function pointers like:
|
||||||
|
# int (*pcs_config)(struct foo)
|
||||||
|
$prototype =~ s@^(\S+\s+)\(\s*\*(\S+)\)@$1$2@gos;
|
||||||
|
|
||||||
if ($prototype =~ /SYSCALL_DEFINE/) {
|
if ($prototype =~ /SYSCALL_DEFINE/) {
|
||||||
syscall_munge();
|
syscall_munge();
|
||||||
}
|
}
|
||||||
|
@ -1836,6 +2017,7 @@ sub process_export_file($) {
|
||||||
|
|
||||||
while (<IN>) {
|
while (<IN>) {
|
||||||
if (/$export_symbol/) {
|
if (/$export_symbol/) {
|
||||||
|
next if (defined($nosymbol_table{$2}));
|
||||||
$function_table{$2} = 1;
|
$function_table{$2} = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1867,7 +2049,7 @@ sub process_name($$) {
|
||||||
if (/$doc_block/o) {
|
if (/$doc_block/o) {
|
||||||
$state = STATE_DOCBLOCK;
|
$state = STATE_DOCBLOCK;
|
||||||
$contents = "";
|
$contents = "";
|
||||||
$new_start_line = $. + 1;
|
$new_start_line = $.;
|
||||||
|
|
||||||
if ( $1 eq "" ) {
|
if ( $1 eq "" ) {
|
||||||
$section = $section_intro;
|
$section = $section_intro;
|
||||||
|
@ -1935,6 +2117,25 @@ sub process_name($$) {
|
||||||
sub process_body($$) {
|
sub process_body($$) {
|
||||||
my $file = shift;
|
my $file = shift;
|
||||||
|
|
||||||
|
# Until all named variable macro parameters are
|
||||||
|
# documented using the bare name (`x`) rather than with
|
||||||
|
# dots (`x...`), strip the dots:
|
||||||
|
if ($section =~ /\w\.\.\.$/) {
|
||||||
|
$section =~ s/\.\.\.$//;
|
||||||
|
|
||||||
|
if ($verbose) {
|
||||||
|
print STDERR "${file}:$.: warning: Variable macro arguments should be documented without dots\n";
|
||||||
|
++$warnings;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($state == STATE_BODY_WITH_BLANK_LINE && /^\s*\*\s?\S/) {
|
||||||
|
dump_section($file, $section, $contents);
|
||||||
|
$section = $section_default;
|
||||||
|
$new_start_line = $.;
|
||||||
|
$contents = "";
|
||||||
|
}
|
||||||
|
|
||||||
if (/$doc_sect/i) { # case insensitive for supported section names
|
if (/$doc_sect/i) { # case insensitive for supported section names
|
||||||
$newsection = $1;
|
$newsection = $1;
|
||||||
$newcontents = $2;
|
$newcontents = $2;
|
||||||
|
@ -1987,19 +2188,23 @@ sub process_body($$) {
|
||||||
$prototype = "";
|
$prototype = "";
|
||||||
$state = STATE_PROTO;
|
$state = STATE_PROTO;
|
||||||
$brcount = 0;
|
$brcount = 0;
|
||||||
|
$new_start_line = $. + 1;
|
||||||
} elsif (/$doc_content/) {
|
} elsif (/$doc_content/) {
|
||||||
# miguel-style comment kludge, look for blank lines after
|
|
||||||
# @parameter line to signify start of description
|
|
||||||
if ($1 eq "") {
|
if ($1 eq "") {
|
||||||
if ($section =~ m/^@/ || $section eq $section_context) {
|
if ($section eq $section_context) {
|
||||||
dump_section($file, $section, $contents);
|
dump_section($file, $section, $contents);
|
||||||
$section = $section_default;
|
$section = $section_default;
|
||||||
$contents = "";
|
$contents = "";
|
||||||
$new_start_line = $.;
|
$new_start_line = $.;
|
||||||
|
$state = STATE_BODY;
|
||||||
} else {
|
} else {
|
||||||
|
if ($section ne $section_default) {
|
||||||
|
$state = STATE_BODY_WITH_BLANK_LINE;
|
||||||
|
} else {
|
||||||
|
$state = STATE_BODY;
|
||||||
|
}
|
||||||
$contents .= "\n";
|
$contents .= "\n";
|
||||||
}
|
}
|
||||||
$state = STATE_BODY;
|
|
||||||
} elsif ($state == STATE_BODY_MAYBE) {
|
} elsif ($state == STATE_BODY_MAYBE) {
|
||||||
# Continued declaration purpose
|
# Continued declaration purpose
|
||||||
chomp($declaration_purpose);
|
chomp($declaration_purpose);
|
||||||
|
@ -2131,7 +2336,7 @@ sub process_file($) {
|
||||||
|
|
||||||
$file = map_filename($orig_file);
|
$file = map_filename($orig_file);
|
||||||
|
|
||||||
if (!open(IN,"<$file")) {
|
if (!open(IN_FILE,"<$file")) {
|
||||||
print STDERR "Error: Cannot open file $file\n";
|
print STDERR "Error: Cannot open file $file\n";
|
||||||
++$errors;
|
++$errors;
|
||||||
return;
|
return;
|
||||||
|
@ -2140,9 +2345,9 @@ sub process_file($) {
|
||||||
$. = 1;
|
$. = 1;
|
||||||
|
|
||||||
$section_counter = 0;
|
$section_counter = 0;
|
||||||
while (<IN>) {
|
while (<IN_FILE>) {
|
||||||
while (s/\\\s*$//) {
|
while (s/\\\s*$//) {
|
||||||
$_ .= <IN>;
|
$_ .= <IN_FILE>;
|
||||||
}
|
}
|
||||||
# Replace tabs by spaces
|
# Replace tabs by spaces
|
||||||
while ($_ =~ s/\t+/' ' x (length($&) * 8 - length($`) % 8)/e) {};
|
while ($_ =~ s/\t+/' ' x (length($&) * 8 - length($`) % 8)/e) {};
|
||||||
|
@ -2151,7 +2356,8 @@ sub process_file($) {
|
||||||
process_normal();
|
process_normal();
|
||||||
} elsif ($state == STATE_NAME) {
|
} elsif ($state == STATE_NAME) {
|
||||||
process_name($file, $_);
|
process_name($file, $_);
|
||||||
} elsif ($state == STATE_BODY || $state == STATE_BODY_MAYBE) {
|
} elsif ($state == STATE_BODY || $state == STATE_BODY_MAYBE ||
|
||||||
|
$state == STATE_BODY_WITH_BLANK_LINE) {
|
||||||
process_body($file, $_);
|
process_body($file, $_);
|
||||||
} elsif ($state == STATE_INLINE) { # scanning for inline parameters
|
} elsif ($state == STATE_INLINE) { # scanning for inline parameters
|
||||||
process_inline($file, $_);
|
process_inline($file, $_);
|
||||||
|
@ -2163,17 +2369,24 @@ sub process_file($) {
|
||||||
}
|
}
|
||||||
|
|
||||||
# Make sure we got something interesting.
|
# Make sure we got something interesting.
|
||||||
if ($initial_section_counter == $section_counter) {
|
if ($initial_section_counter == $section_counter && $
|
||||||
if ($output_mode ne "none") {
|
output_mode ne "none") {
|
||||||
|
if ($output_selection == OUTPUT_INCLUDE) {
|
||||||
|
print STDERR "${file}:1: warning: '$_' not found\n"
|
||||||
|
for keys %function_table;
|
||||||
|
}
|
||||||
|
else {
|
||||||
print STDERR "${file}:1: warning: no structured comments found\n";
|
print STDERR "${file}:1: warning: no structured comments found\n";
|
||||||
}
|
}
|
||||||
if (($output_selection == OUTPUT_INCLUDE) && ($show_not_found == 1)) {
|
|
||||||
print STDERR " Was looking for '$_'.\n" for keys %function_table;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
close IN_FILE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if ($output_mode eq "rst") {
|
||||||
|
get_sphinx_version() if (!$sphinx_major);
|
||||||
|
}
|
||||||
|
|
||||||
$kernelversion = get_kernel_version();
|
$kernelversion = get_kernel_version();
|
||||||
|
|
||||||
# generate a sequence of code that will splice in highlighting information
|
# generate a sequence of code that will splice in highlighting information
|
||||||
|
@ -2220,4 +2433,9 @@ if ($verbose && $warnings) {
|
||||||
print STDERR "$warnings warnings\n";
|
print STDERR "$warnings warnings\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
exit($output_mode eq "none" ? 0 : $errors);
|
if ($Werror && $warnings) {
|
||||||
|
print STDERR "$warnings warnings as Errors\n";
|
||||||
|
exit($warnings);
|
||||||
|
} else {
|
||||||
|
exit($output_mode eq "none" ? 0 : $errors)
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue