mirror of
https://github.com/lvgl/lvgl.git
synced 2026-05-21 05:51:45 +08:00
docs(api_doc_builder.py): fix frail assumptions in API-page generation (#8694)
This commit is contained in:
+5
-5
@@ -815,11 +815,11 @@ INPUT_ENCODING = UTF-8
|
||||
# *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.pyw, *.f90, *.f95, *.f03, *.f08,
|
||||
# *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf and *.qsf.
|
||||
|
||||
FILE_PATTERNS = lv*.h \
|
||||
*.hh \
|
||||
*.hxx \
|
||||
*.hpp \
|
||||
*.h++
|
||||
FILE_PATTERNS = lv*.h \
|
||||
lv*.hh \
|
||||
lv*.hxx \
|
||||
lv*.hpp \
|
||||
lv*.h++
|
||||
|
||||
# The RECURSIVE tag can be used to specify whether or not subdirectories should
|
||||
# be searched for input files as well.
|
||||
|
||||
+130
-23
@@ -7,6 +7,46 @@ Uses doxygen_xml.py module to:
|
||||
- Prep and run Doxygen
|
||||
- make Doxygen XML output available, and
|
||||
- make Doxygen-documented symbols from the C code available.
|
||||
|
||||
Because these 3 files are acceptable in a C project:
|
||||
|
||||
1. ./path/to/one/aaa.c
|
||||
2. ./path/to/one/aaa.h
|
||||
3. ./path/to/one/aaa.cpp
|
||||
4. ./path/to/one/aaa.hpp
|
||||
5. ./path/to/another/aaa.h
|
||||
|
||||
we see that:
|
||||
|
||||
- duplicate filename stems ('aaa' above) are acceptable, and
|
||||
- they must not only be differentiated by their extensions, but also by their path.
|
||||
|
||||
On the other hand, Sphinx link reference names for :ref:`link_ref_name` link
|
||||
references must be unique throughout a document project. Since API pages are
|
||||
generated from C source files, to make this effective, they must include:
|
||||
|
||||
A. at least part of the path,
|
||||
B. filename stem, and
|
||||
C. extension
|
||||
|
||||
in the link reference names. Prior to 11-Aug-2025, link reference names were formed
|
||||
using ONLY the filename stem. This created a conflict when an example of #4 above
|
||||
appeared in the LVGL code, and caused a doc-build failure because the API-page link
|
||||
reference names to #2 and #4 were identical, and Sphinx (correctly) does not allow
|
||||
that. So after 11-Aug-2025, these link reference names are now differentiated all 3
|
||||
of A, B and C above.
|
||||
|
||||
.. note::
|
||||
|
||||
Since they are generated and dealt with programmatically, using the full path
|
||||
[below the docs source directory] will not be a problem. Even though it makes
|
||||
the names long, they never have to be hand-typed.
|
||||
|
||||
Likewise, API-page ``.rst`` filename stems also need to be unique within the directory
|
||||
they are in, so the stems must contain the C source file's filename stem plus the
|
||||
extension in them, in order to be unique in that directory, since they *must* end in
|
||||
``.rst``.
|
||||
|
||||
"""
|
||||
import os
|
||||
import re
|
||||
@@ -16,6 +56,17 @@ from announce import *
|
||||
old_html_files = {}
|
||||
EMIT_WARNINGS = True
|
||||
rst_section_line_char = '='
|
||||
on_windows = (( os.name == 'nt' ))
|
||||
# `doxy_src_file_ext_list` must
|
||||
# - match the extensions in the Doxyfile FILE_PATTERNS list, and
|
||||
# - be in lower case.
|
||||
doxy_src_file_ext_list = [
|
||||
'.h',
|
||||
'.hh',
|
||||
'.hxx',
|
||||
'.hpp',
|
||||
'.h++'
|
||||
]
|
||||
|
||||
# Multi-line match ``API + newline + \*\*\* + whitespace``.
|
||||
# NB: the ``\s*`` at the end forces the regex to match the whitespace
|
||||
@@ -34,7 +85,7 @@ _re_api_startswith = re.compile(r'(?mi)^\s*\.\.\s+API\s+startswith:\s*([\w,\s]+)
|
||||
_re_multi_line_comma_sep = re.compile(r'(?m)[,\s]+')
|
||||
|
||||
# Regex to identify editor-added hyperlinks: :ref:`lv_obj_h`
|
||||
_re_editor_added_hyperlink = re.compile(r'^\s*:ref:`(\w+)`')
|
||||
_re_editor_added_hyperlink = re.compile(r'^\s*:ref:`([\w/\.]+)`')
|
||||
|
||||
# Separator to mark place where this script added hyperlinks.
|
||||
_auto_gen_sep = '.. Autogenerated'
|
||||
@@ -140,7 +191,15 @@ def _conditionally_add_hyperlink(obj, genned_link_set: set, exclude_set: set):
|
||||
:return:
|
||||
"""
|
||||
if obj.file_name is not None:
|
||||
link_name = os.path.basename(obj.file_name).replace('.', '_')
|
||||
# The link name should look like this: draw/sw/blend/lv_draw_sw_blend.h
|
||||
# including when running under Windows.
|
||||
# `obj.file_name` contains a path that looks like this:
|
||||
# '/path/to/lvgl/src/draw/sw/blend/lv_draw_sw_blend.h'
|
||||
# Note: we can't use 'lvgl/src' because there is no guarantee
|
||||
# that the user's LVGL repo directory will be called that.
|
||||
src_root = '/src/'
|
||||
i = obj.file_name.index(src_root) + len(src_root)
|
||||
link_name = obj.file_name[i:]
|
||||
if link_name not in genned_link_set:
|
||||
if link_name not in exclude_set:
|
||||
genned_link_set.add(link_name)
|
||||
@@ -191,7 +250,7 @@ def _add_exact_matches(symbols: [str], genned_link_set, editor_link_set):
|
||||
|
||||
|
||||
def _hyperlink_sort_value(init_value: str):
|
||||
if init_value.endswith('_h'):
|
||||
if init_value.endswith('.h'):
|
||||
result = init_value[:-2]
|
||||
else:
|
||||
result = init_value
|
||||
@@ -544,6 +603,12 @@ def _add_hyperlinks_to_eligible_files(intermediate_dir: str,
|
||||
f.write(data.encode('utf-8'))
|
||||
|
||||
|
||||
def _extended_stem_from_path(path: str) -> str:
|
||||
filename = os.path.basename(path)
|
||||
stem, ext = os.path.splitext(filename)
|
||||
return stem + '_' + ext[1:] # Remove leading '.' from ext.
|
||||
|
||||
|
||||
def _create_rst_files_for_dir(src_root_dir_len: int,
|
||||
src_dir_bep: str,
|
||||
elig_h_files: [str],
|
||||
@@ -558,14 +623,40 @@ def _create_rst_files_for_dir(src_root_dir_len: int,
|
||||
- add reference "sub_dir_name/index" in `index.rst`.
|
||||
|
||||
:param src_root_dir_len: Length of source-root path string, used with `out_root_dir` to build paths
|
||||
Example: 17
|
||||
:param src_dir_bep: Directory currently *being processed*
|
||||
:param elig_h_files: Eligible `.h` files directly contained in `src_dir_bep`
|
||||
:param elig_sub_dirs: List of sub-dirs that contained eligible `.h` files
|
||||
Example: '/path/to/lvgl/src/draw/sw/blend'
|
||||
:param elig_h_files: Eligible `.h*` files directly contained in `src_dir_bep`
|
||||
Example: [] or
|
||||
[
|
||||
'/path/to/lvgl/src/draw/sw/blend/lv_draw_sw_blend.h',
|
||||
'/path/to/lvgl/src/draw/sw/blend/lv_draw_sw_blend_private.h',
|
||||
'/path/to/lvgl/src/draw/sw/blend/lv_draw_sw_blend_to_al88.h',
|
||||
'/path/to/lvgl/src/draw/sw/blend/lv_draw_sw_blend_to_argb8888.h',
|
||||
'/path/to/lvgl/src/draw/sw/blend/lv_draw_sw_blend_to_argb8888_premultiplied.h',
|
||||
'/path/to/lvgl/src/draw/sw/blend/lv_draw_sw_blend_to_i1.h',
|
||||
'/path/to/lvgl/src/draw/sw/blend/lv_draw_sw_blend_to_l8.h',
|
||||
'/path/to/lvgl/src/draw/sw/blend/lv_draw_sw_blend_to_rgb565.h',
|
||||
'/path/to/lvgl/src/draw/sw/blend/lv_draw_sw_blend_to_rgb565_swapped.h',
|
||||
'/path/to/lvgl/src/draw/sw/blend/lv_draw_sw_blend_to_rgb888.h'
|
||||
]
|
||||
:param elig_sub_dirs: List of sub-dirs that contained eligible `.h*` files
|
||||
Example: [] or
|
||||
[
|
||||
'/path/to/lvgl/src/draw/sw/blend/arm2d',
|
||||
'/path/to/lvgl/src/draw/sw/blend/helium',
|
||||
'/path/to/lvgl/src/draw/sw/blend/neon'
|
||||
]
|
||||
:param out_root_dir: Root of output directory, used with to build paths.
|
||||
Example: '/path/to/lvgl/src/docs/intermediate/API'
|
||||
:return: n/a
|
||||
"""
|
||||
indent = ' '
|
||||
# Keep only sub-path under 'lvgl/src/'.
|
||||
# Example: 'draw/sw/blend'
|
||||
sub_path = src_dir_bep[src_root_dir_len:]
|
||||
# Build full path to output directory for 'index.rst' file.
|
||||
# Example: '/path/to/lvgl/src/docs/intermediate/API/draw/sw/blend'
|
||||
out_dir = str(os.path.join(out_root_dir, sub_path))
|
||||
|
||||
# Ensure dir exists. Multiple dirs MAY have to be created
|
||||
@@ -591,9 +682,8 @@ def _create_rst_files_for_dir(src_root_dir_len: int,
|
||||
|
||||
# One entry per `.rst` file
|
||||
for h_file in elig_h_files:
|
||||
filename = os.path.basename(h_file)
|
||||
stem = os.path.splitext(filename)[0]
|
||||
f.write(indent + stem + '\n')
|
||||
extended_stem = _extended_stem_from_path(h_file)
|
||||
f.write(indent + extended_stem + '\n')
|
||||
|
||||
# One entry per eligible subdirectory.
|
||||
for sub_dir in elig_sub_dirs:
|
||||
@@ -603,14 +693,28 @@ def _create_rst_files_for_dir(src_root_dir_len: int,
|
||||
# One .rst file per h_file
|
||||
for h_file in elig_h_files:
|
||||
filename = os.path.basename(h_file)
|
||||
stem = os.path.splitext(filename)[0]
|
||||
rst_file = os.path.join(out_dir, stem + '.rst')
|
||||
html_file = os.path.join(sub_path, stem + '.html')
|
||||
old_html_files[stem] = html_file
|
||||
extended_stem = _extended_stem_from_path(h_file)
|
||||
rst_file = os.path.join(out_dir, extended_stem + '.rst')
|
||||
html_file = os.path.join(sub_path, extended_stem + '.html')
|
||||
old_html_files[extended_stem] = html_file
|
||||
|
||||
with open(rst_file, 'w') as f:
|
||||
# Sphinx link target.
|
||||
f.write(f'.. _{stem}_h:\n\n')
|
||||
# The link name should look like this: draw/sw/blend/lv_draw_sw_blend.h
|
||||
# including when running under Windows.
|
||||
if on_windows:
|
||||
link_name_path = sub_path.replace(os.sep, '/')
|
||||
else:
|
||||
link_name_path = sub_path
|
||||
|
||||
if len(link_name_path) > 0:
|
||||
link_name = link_name_path + '/' + filename
|
||||
else:
|
||||
link_name = filename
|
||||
|
||||
line = f'.. _{link_name}:\n\n'
|
||||
|
||||
f.write(line)
|
||||
# Doc title.
|
||||
section_line = (rst_section_line_char * len(filename)) + '\n'
|
||||
f.write(section_line)
|
||||
@@ -631,7 +735,7 @@ def _recursively_create_api_rst_files(depth: int,
|
||||
recursively for subdirectories below it. ("bep" = being processed.)
|
||||
|
||||
Eligible
|
||||
An input file (e.g. `.h` or `.c`) file is eligible if Doxygen generated
|
||||
An input file (e.g. `.h*`) file is eligible if Doxygen generated
|
||||
documentation for it. The combination of these configuration items in
|
||||
the Doxyfile:
|
||||
|
||||
@@ -646,12 +750,12 @@ def _recursively_create_api_rst_files(depth: int,
|
||||
file depends upon whether any eligible files were recursively
|
||||
found within it. And that isn't known until this function finishes
|
||||
(recursively) processing a directory and returns the number of
|
||||
eligible `.h` files found in its subdirectory tree. Thus, the steps
|
||||
eligible `.h*` files found in its subdirectory tree. Thus, the steps
|
||||
taken within are:
|
||||
|
||||
- Discover all eligible `.h` files directly contained in `src_dir_bep`.
|
||||
- Discover all eligible `.h*` files directly contained in `src_dir_bep`.
|
||||
- Recursively do the same for each subdirectory, adding the returned
|
||||
count of eligible `.h` files to the sum (`elig_h_file_count`).
|
||||
count of eligible `.h*` files to the sum (`elig_h_file_count`).
|
||||
- If `elig_h_file_count > 0`:
|
||||
- call _create_rst_files_for_dir() to generate appropriate
|
||||
`.rst` files for this directory.
|
||||
@@ -665,9 +769,9 @@ def _recursively_create_api_rst_files(depth: int,
|
||||
:param src_root_len: Length of source-root path
|
||||
:param src_dir_bep: Source directory *being processed*
|
||||
:param out_root_dir: Output root directory (used to build output paths)
|
||||
:return: Number of `.h` files encountered (so caller knows
|
||||
:return: Number of `.h*` files encountered (so caller knows
|
||||
whether that directory recursively held any
|
||||
eligible `.h` files, to know whether to include
|
||||
eligible `.h*` files, to know whether to include
|
||||
"subdir/index" in caller's local `index.rst` file).
|
||||
"""
|
||||
elig_h_files = []
|
||||
@@ -686,7 +790,10 @@ def _recursively_create_api_rst_files(depth: int,
|
||||
if os.path.isdir(path_bep):
|
||||
sub_dirs.append(path_bep) # Add to sub-dir list.
|
||||
else:
|
||||
if dir_item.lower().endswith('.h'):
|
||||
_, ext = os.path.splitext(dir_item)
|
||||
# `ext` is converted to lower case so that any incidental case change
|
||||
# in the extension on Windows will not break this algorithm.
|
||||
if ext.lower() in doxy_src_file_ext_list:
|
||||
eligible = (dir_item in doxygen_xml.files)
|
||||
if eligible:
|
||||
elig_h_files.append(path_bep) # Add file to list.
|
||||
@@ -711,7 +818,7 @@ def _recursively_create_api_rst_files(depth: int,
|
||||
elig_sub_dirs.sort()
|
||||
elig_h_files.sort()
|
||||
|
||||
# Create index.rst plus .RST files for any .H file directly in in dir.
|
||||
# Create index.rst plus .RST files for any .H* file directly in in dir.
|
||||
_create_rst_files_for_dir(src_root_len,
|
||||
src_dir_bep,
|
||||
elig_h_files,
|
||||
@@ -723,9 +830,9 @@ def _recursively_create_api_rst_files(depth: int,
|
||||
|
||||
def create_api_rst_files(src_root_dir: str, out_root_dir: str):
|
||||
"""
|
||||
Create `.rst` files for API pages based on the `.h` files found
|
||||
Create `.rst` files for API pages based on the `.h*` files found
|
||||
in a tree-walk of `a_src_root` and the current contents of the
|
||||
`doxygen_xml.files` dictionary (used to filter out `.h` files that
|
||||
`doxygen_xml.files` dictionary (used to filter out `.h*` files that
|
||||
Doxygen generated no documentation for). Output the `.rst` files
|
||||
into `out_root_dir` mirroring the `a_src_root` directory structure.
|
||||
|
||||
|
||||
+1
-1
@@ -743,7 +743,7 @@ def run(args):
|
||||
if debugging_breathe:
|
||||
from sphinx.cmd.build import main as sphinx_build
|
||||
# Don't allow parallel processing while debugging (the '-j' arg is removed).
|
||||
sphinx_args = ['-M', 'html', f'{src}', f'{dst}', '-D', f'version={ver}']
|
||||
sphinx_args = ['-M', 'html', f'{src}', f'{dst}']
|
||||
|
||||
if len(env_opt) > 0:
|
||||
sphinx_args.append(f'{env_opt}')
|
||||
|
||||
@@ -1606,6 +1606,7 @@ class DoxygenXml(object):
|
||||
'LV_ATTRIBUTE_FAST_MEM=',
|
||||
'LV_ATTRIBUTE_EXTERN_DATA=',
|
||||
'LV_FORMAT_ATTRIBUTE(fmt,va)=',
|
||||
'FASTGLTF_EXPORT=',
|
||||
]
|
||||
|
||||
cfg.set('PREDEFINED', predefined_symbols)
|
||||
@@ -1631,9 +1632,14 @@ class DoxygenXml(object):
|
||||
full_path = os.path.join(lvgl_src_dir, osal_dir, osal_h)
|
||||
exclude_paths.append(full_path)
|
||||
|
||||
# Exclude as a workaround for Breathe parsing problems.
|
||||
full_path = os.path.join(lvgl_src_dir, 'core', 'lv_obj_property.h')
|
||||
exclude_paths.append(full_path)
|
||||
|
||||
# Exclude GLTF templates that Breathe appears to not know how to parse.
|
||||
full_path = os.path.join(lvgl_src_dir, 'libs', 'gltf', 'fastgltf', 'lv_fastgltf.hpp')
|
||||
exclude_paths.append(full_path)
|
||||
|
||||
cfg.set('EXCLUDE', exclude_paths)
|
||||
|
||||
# Include TAGFILES if requested.
|
||||
|
||||
+3
-2
@@ -38,9 +38,10 @@ if "%SPHINXOPTS%" == "" (
|
||||
rem python ./src/lvgl_version.py >_version_temp.txt
|
||||
rem set /p VER=<_version_temp.txt
|
||||
rem del _version_temp.txt
|
||||
for /F %%v in ('python lvgl_version.py') do set VER=%%v
|
||||
for /F %%v in ('python src\lvgl_version.py') do set VER=%%v
|
||||
echo VERSION [!VER!]
|
||||
set SPHINXOPTS=-D version="!VER!" -j 4
|
||||
rem set SPHINXOPTS=-D version="!VER!" -j 4
|
||||
set SPHINXOPTS=-j 4
|
||||
set VER=
|
||||
)
|
||||
|
||||
|
||||
@@ -117,7 +117,8 @@ by getting the Y coordinate of a child.
|
||||
int32_t y_end = lv_obj_get_y(child);
|
||||
if(y_start + 100 != y_end) fail();
|
||||
|
||||
Please refer to :ref:`lv_test_indev_h` for the list of supported input device emulation functions.
|
||||
Please refer to :ref:`others/test/lv_test_indev.h` for the list of supported input
|
||||
device emulation functions.
|
||||
|
||||
Screenshot Comparison
|
||||
---------------------
|
||||
|
||||
@@ -159,4 +159,5 @@ See :ref:`indev_creation` to see how to do this.
|
||||
|
||||
API
|
||||
***
|
||||
:ref:`lv_tick_h`
|
||||
|
||||
.. API startswith: lv_tick_
|
||||
|
||||
@@ -65,6 +65,6 @@ Examples
|
||||
API
|
||||
***
|
||||
|
||||
:ref:`lv_draw_sw_arm2d_h`
|
||||
:ref:`draw/sw/arm2d/lv_draw_sw_arm2d.h`
|
||||
|
||||
:ref:`lv_blend_arm2d_h`
|
||||
:ref:`draw/sw/blend/arm2d/lv_blend_arm2d.h`
|
||||
|
||||
@@ -5,4 +5,4 @@ Espressif Pixel Processing Accelerator
|
||||
API
|
||||
***
|
||||
|
||||
:ref:`lv_draw_ppa_h`
|
||||
.. API startswith: lv_draw_ppa_
|
||||
|
||||
@@ -109,6 +109,6 @@ See the LVGL :ref:`DMA2D support <dma2d>`.
|
||||
API
|
||||
***
|
||||
|
||||
:ref:`lv_draw_nema_gfx_h`
|
||||
.. API startswith: lv_draw_nema_
|
||||
|
||||
:ref:`lv_draw_nema_gfx_utils_h`
|
||||
.. API startswith: lv_nemagfx_
|
||||
|
||||
@@ -5,4 +5,4 @@ NXP G2D GPU
|
||||
API
|
||||
***
|
||||
|
||||
:ref:`lv_draw_g2d_h`
|
||||
.. API startswith: lv_draw_g2d_
|
||||
|
||||
@@ -5,6 +5,6 @@ NXP PXP GPU
|
||||
API
|
||||
***
|
||||
|
||||
:ref:`lv_draw_pxp_h`
|
||||
.. API startswith: lv_draw_pxp_
|
||||
|
||||
:ref:`lv_pxp_cfg_h`
|
||||
.. API startswith: lv_pxp_
|
||||
|
||||
@@ -5,4 +5,4 @@ NXP VGLite GPU
|
||||
API
|
||||
***
|
||||
|
||||
:ref:`lv_draw_vglite_h`
|
||||
.. API startswith: lv_draw_vglite_
|
||||
|
||||
@@ -5,5 +5,5 @@ SDL Renderer
|
||||
API
|
||||
***
|
||||
|
||||
:ref:`lv_draw_sdl_h`
|
||||
.. API startswith: lv_draw_sdl_
|
||||
|
||||
|
||||
@@ -5,5 +5,5 @@ STM32 DMA2D GPU
|
||||
API
|
||||
***
|
||||
|
||||
:ref:`lv_draw_dma2d_h`
|
||||
.. API startswith: lv_draw_dma2d_
|
||||
|
||||
|
||||
@@ -73,14 +73,13 @@ For detailed instructions, see :ref:`vg_lite_tvg`.
|
||||
API
|
||||
***
|
||||
|
||||
:ref:`lv_draw_vglite_h`
|
||||
.. API startswith: lv_draw_vglite_
|
||||
|
||||
:ref:`lv_vglite_buf_h`
|
||||
.. API equals: vglite_set_buf
|
||||
|
||||
:ref:`lv_vglite_matrix_h`
|
||||
.. API equals: vglite_set_translation_matrix
|
||||
|
||||
:ref:`lv_vglite_path_h`
|
||||
|
||||
:ref:`lv_vglite_utils_h`
|
||||
.. API equals: vglite_create_rect_path_data
|
||||
|
||||
.. API equals: vglite_get_color
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@ Example
|
||||
API
|
||||
***
|
||||
|
||||
:ref:`lv_fsdrv_h`
|
||||
:ref:`libs/fsdrv/lv_fsdrv.h`
|
||||
|
||||
See also: `lvgl/src/libs/fsdrv/lv_fs_littlefs.c <https://github.com/lvgl/lvgl/blob/master/src/libs/fsdrv/lv_fs_littlefs.c>`__
|
||||
|
||||
|
||||
@@ -76,5 +76,5 @@ Example
|
||||
API
|
||||
***
|
||||
|
||||
:ref:`lv_libjpeg_turbo_h`
|
||||
:ref:`libs/libjpeg_turbo/lv_libjpeg_turbo.h`
|
||||
|
||||
|
||||
@@ -101,4 +101,4 @@ the output to ``./output/cogwheel.bin``.
|
||||
API
|
||||
***
|
||||
|
||||
:ref:`lv_rle_h`
|
||||
:ref:`libs/rle/lv_rle.h`
|
||||
|
||||
@@ -26,4 +26,4 @@ Display (lv_display)
|
||||
API
|
||||
***
|
||||
|
||||
:ref:`lv_display_h`
|
||||
:ref:`display/lv_display.h`
|
||||
|
||||
Reference in New Issue
Block a user