mirror of
https://github.com/lvgl/lvgl.git
synced 2026-02-07 06:42:17 +08:00
789 lines
34 KiB
Python
Executable File
789 lines
34 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
""" build.py -- Generate LVGL documentation using Doxygen and Sphinx + Breathe.
|
|
|
|
Data Flow
|
|
---------
|
|
|
|
.. code-block:: text
|
|
|
|
Inputs Generated Source Files Output
|
|
----------- ---------------------- ----------------------
|
|
./docs/src/ \
|
|
./src/ >===> ./docs/intermediate/ ===> ./docs/build/<format>/
|
|
./examples/ /
|
|
|
|
Once ./docs/intermediate/ is built, you can use all the Sphinx output
|
|
formats, e.g.
|
|
|
|
- make html
|
|
- make latex
|
|
- make man
|
|
- make htmlhelp
|
|
- etc.
|
|
|
|
|
|
Description
|
|
-----------
|
|
Copy source files to an intermediate directory and modify them there before
|
|
doc generation occurs. If a full rebuild is being done (e.g. after a `clean`)
|
|
run Doxygen on LVGL's source files to generate intermediate API information
|
|
in XML format. Generate API documents for Breathe's consumption. Add API
|
|
links to end of some documents. Generate example documents. From there,
|
|
Sphinx with Breathe extension uses the resulting set of intermediate files
|
|
to generate the desired output.
|
|
|
|
It is only during this first build that the `--skip-api` option has meaning.
|
|
After the first build, no further actions is taken regarding API pages since
|
|
they are not regenerated after the first build.
|
|
|
|
The intermediate directory has a fixed location (overridable by
|
|
`LVGL_DOC_BUILD_INTERMEDIATE_DIR` environment variable) and by default this
|
|
script attempts to rebuild only those documents whose path, name or
|
|
modification date has changed since the last build.
|
|
|
|
The output directory also has a fixed location (overridable by
|
|
`LVGL_DOC_BUILD_OUTPUT_DIR` environment variable).
|
|
|
|
Caution:
|
|
|
|
The document build meant for end-user consumption should ONLY be done after a
|
|
`--clean` unless you know that no API documentation and no code examples have changed.
|
|
|
|
A `sphinx-build` will do a full doc rebuild any time:
|
|
|
|
- the intermediate directory doesn't exist or is empty (since the new files in
|
|
the intermediate directory will have modification times after the generated
|
|
HTML or Latex files, even if nothing changed),
|
|
- the targeted output directory doesn't exist or is empty, or
|
|
- Sphinx determines that a full rebuild is necessary. This happens when:
|
|
- intermediate directory path (Sphinx's source-file path) has changed,
|
|
- any options on the `sphinx-build` command line have changed,
|
|
- `conf.py` modification date has changed, or
|
|
- `--fresh-env` argument is included (runs `sphinx-build` with -E option).
|
|
|
|
Typical run time:
|
|
|
|
Full build: 22.5 min
|
|
--skip-api: 1.9 min (applies to first build only)
|
|
|
|
|
|
Usage
|
|
-----
|
|
usage: build.py [-h] [-s] [-E]
|
|
{html,latex,intermediate,clean,clean-intermediate,clean-html,clean-latex}
|
|
[{html,latex,intermediate,clean,clean-intermediate,clean-html,clean-latex} ...]
|
|
|
|
Build LVGL documents
|
|
|
|
positional arguments:
|
|
{html,latex,intermediate,clean,clean-intermediate,clean-html,clean-latex}
|
|
output targets to generate; one or more of these;
|
|
`clean...` targets are completed first
|
|
|
|
options:
|
|
-h, --help show this help message and exit
|
|
-s, --skip-api skip API-page generation
|
|
-E, --fresh-env rebuild Sphinx environment
|
|
|
|
|
|
Options
|
|
-------
|
|
--help
|
|
Print usage note and exit with status 0.
|
|
|
|
Targets:
|
|
|
|
html [ --skip-api ] [ --fresh-env ]
|
|
Build HTML output.
|
|
`--skip-api` only has effect on first build after a `clean` or `clean_intermediate`.
|
|
|
|
latex [ --skip-api ] [ --fresh-env ]
|
|
Build Latex/PDF output (on hold pending removal of non-ASCII characters from input files).
|
|
`--skip-api` only has effect on first build after a `clean` or `clean_intermediate`.
|
|
|
|
intermediate [ --skip-api ]
|
|
Generate intermediate directory contents (ready to build output formats).
|
|
If they already exist, they are removed and re-generated.
|
|
Note: "intermediate" can be abbreviated down to "int".
|
|
|
|
clean
|
|
Remove all generated files.
|
|
|
|
clean-intermediate
|
|
Remove intermediate directory.
|
|
Note: "clean_intermediate" can be abbreviated down to "clean_int".
|
|
|
|
clean-html
|
|
Remove HTML output directory.
|
|
|
|
clean-latex
|
|
Remove Latex output directory.
|
|
|
|
Options with Output Targets:
|
|
|
|
--skip-api (-s) (with `html` and/or `latex` and/or `intermediate` options)
|
|
Skip API pages and links when intermediate directory contents are being generated
|
|
(saving about 91% of build time). Note: they are not thereafter regenerated unless
|
|
requested by `intermediate` argument or the intermediate directory does not
|
|
exist. This is intended to be used only during doc development to speed up
|
|
turn-around time between doc modifications and seeing final results.
|
|
|
|
--fresh-env (-E) (with `html` and/or `latex` options)
|
|
Run `sphinx-build` with -E command-line argument, which makes it regenerate its
|
|
"environment" (memory of what was built previously, forcing a full rebuild).
|
|
|
|
Unrecognized arguments print error message, usage note, and exit with status 1.
|
|
|
|
|
|
Python Package Requirements
|
|
---------------------------
|
|
The list of Python package requirements are in `requirements.txt`.
|
|
|
|
Install them by:
|
|
|
|
$ pip install -r requirements.txt
|
|
|
|
|
|
History
|
|
-------
|
|
The first version of this file (Apr 2021) discovered the name of
|
|
the current branch (e.g. 'master', 'release/v8.4', etc.) to support
|
|
different versions of the documentation by establishing the base URL
|
|
(used in `conf.py` and in [Edit on GitHub] links), and then ran:
|
|
|
|
- Doxygen (to generate LVGL API XML), then
|
|
- Sphinx
|
|
|
|
to generate the LVGL document tree. Internally, Sphinx uses `breathe`
|
|
(a Sphinx extension) to provide a bridge between Doxygen XML output and
|
|
Sphinx documentation. It also supported a command-line option `--clean`
|
|
to remove generated files before starting (eliminates orphan files,
|
|
for docs that have moved or changed).
|
|
|
|
Since then its duties have grown to include:
|
|
|
|
- Using environment variables to convey branch names to several more
|
|
places where they are used in the docs-generating process (instead
|
|
of re-writing `conf.py` and a `header.rst` each time docs were
|
|
generated). These are documented where they generated below.
|
|
|
|
- Supporting additional command-line options.
|
|
|
|
- Generating a `./docs/lv_conf.h` for Doxygen to use (config_builder.py).
|
|
|
|
- Supporting multiple execution platforms (which then required tokenizing
|
|
Doxygen's INPUT path in `Doxyfile` and re-writing portions that used
|
|
`sed` to generate input or modify files).
|
|
|
|
- Adding translation and API links (requiring generating docs in an
|
|
intermediate directory so that the links could be programmatically
|
|
added to each document before Sphinx was run). Note: translation links
|
|
are now done manually since they are only on the main landing page.
|
|
|
|
- Generating EXAMPLES page. Specific example groups are added to individual
|
|
documents by each document having an ``.. include:: /examples/.../index.rst``
|
|
directive where the example(s) should go.
|
|
|
|
- Building PDF via latex.
|
|
|
|
- Shifting doc-generation paradigm to behave more like `make`.
|
|
|
|
"""
|
|
|
|
# ****************************************************************************
|
|
# IMPORTANT: If you are getting a PDF-lexer error for an example, check
|
|
# for extra lines at the end of the file. Only a single empty line
|
|
# is allowed!!! Ask me how long it took me to figure this out.
|
|
# -- @kdschlosser
|
|
# ****************************************************************************
|
|
|
|
# Python Library
|
|
import sys
|
|
import os
|
|
import subprocess
|
|
import shutil
|
|
import dirsync
|
|
import argparse
|
|
from datetime import datetime
|
|
|
|
# LVGL Custom
|
|
import example_list
|
|
import api_doc_builder
|
|
import config_builder
|
|
from src.lvgl_version import lvgl_version
|
|
from announce import *
|
|
|
|
# Not Currently Used
|
|
# (Code is kept in case we want to re-implement it later.)
|
|
# import add_translation
|
|
|
|
# -------------------------------------------------------------------------
|
|
# Configuration
|
|
# -------------------------------------------------------------------------
|
|
# These are relative paths from the ./docs/ directory.
|
|
cfg_project_dir = '..'
|
|
cfg_lvgl_src_dir = 'src'
|
|
cfg_doc_src_dir = 'src'
|
|
cfg_examples_dir = 'examples'
|
|
cfg_default_intermediate_dir = 'intermediate'
|
|
cfg_default_output_dir = 'build'
|
|
cfg_static_dir = '_static'
|
|
cfg_downloads_dir = 'downloads'
|
|
cfg_lv_conf_filename = 'lv_conf.h'
|
|
cfg_lv_version_filename = 'lv_version.h'
|
|
cfg_doxyfile_filename = 'Doxyfile'
|
|
cfg_top_index_filename = 'index.rst'
|
|
cfg_default_branch = 'master'
|
|
cfg_target_html = 'html'
|
|
cfg_target_latex = 'latex'
|
|
cfg_target_intermediate = 'intermediate'
|
|
cfg_target_clean_all = 'clean'
|
|
cfg_target_clean_intermediate = 'clean-intermediate'
|
|
cfg_target_clean_html = 'clean-html'
|
|
cfg_target_clean_latex = 'clean-latex'
|
|
|
|
# Filename generated in `latex_output_dir` and copied to `pdf_output_dir`.
|
|
cfg_pdf_filename = 'LVGL.pdf'
|
|
|
|
|
|
def remove_dir(tgt_dir):
|
|
"""Remove directory `tgt_dir`."""
|
|
if os.path.isdir(tgt_dir):
|
|
announce(__file__, f'Removing {tgt_dir}...')
|
|
shutil.rmtree(tgt_dir)
|
|
else:
|
|
announce(__file__, f'{tgt_dir} already removed...')
|
|
|
|
|
|
def cmd(cmd_str, start_dir=None, exit_on_error=True):
|
|
"""Run external command and abort build on error."""
|
|
saved_dir = None
|
|
|
|
if start_dir is not None:
|
|
saved_dir = os.getcwd()
|
|
os.chdir(start_dir)
|
|
|
|
announce(__file__, f'Running [{cmd_str}] in [{os.getcwd()}]...')
|
|
return_code = os.system(cmd_str)
|
|
|
|
if saved_dir is not None:
|
|
os.chdir(saved_dir)
|
|
|
|
if return_code != 0 and exit_on_error:
|
|
announce(__file__, "Exiting build due to previous error.")
|
|
sys.exit(1)
|
|
|
|
|
|
def intermediate_dir_contents_exists(intermediate_dir):
|
|
"""Provide answer to question: Can we have reasonable confidence that
|
|
the contents of `intermediate_directory` already exists?
|
|
"""
|
|
result = False
|
|
c1 = os.path.isdir(intermediate_dir)
|
|
|
|
if c1:
|
|
temp_path = os.path.join(intermediate_dir, 'CHANGELOG.rst')
|
|
c2 = os.path.exists(temp_path)
|
|
temp_path = os.path.join(intermediate_dir, '_ext')
|
|
c3 = os.path.isdir(temp_path)
|
|
temp_path = os.path.join(intermediate_dir, '_static')
|
|
c4 = os.path.isdir(temp_path)
|
|
temp_path = os.path.join(intermediate_dir, 'debugging')
|
|
c5 = os.path.isdir(temp_path)
|
|
temp_path = os.path.join(intermediate_dir, 'introduction')
|
|
c6 = os.path.isdir(temp_path)
|
|
temp_path = os.path.join(intermediate_dir, 'contributing')
|
|
c7 = os.path.isdir(temp_path)
|
|
temp_path = os.path.join(intermediate_dir, cfg_examples_dir)
|
|
c8 = os.path.isdir(temp_path)
|
|
result = c2 and c3 and c4 and c5 and c6 and c7 and c8
|
|
|
|
return result
|
|
|
|
|
|
def run():
|
|
"""Perform doc-build function(s) requested."""
|
|
|
|
def print_setting(setting_name, val):
|
|
"""Print one setting; used for debugging."""
|
|
announce(__file__, f'{setting_name:18} = [{val}]')
|
|
|
|
def print_settings(args, and_exit: bool):
|
|
"""Print all settings and optionally exit; used for debugging.
|
|
|
|
This routine has proven useful for verification and validation,
|
|
and is being kept for future debugging purposes.
|
|
"""
|
|
# Targets
|
|
print_setting("build_html", cfg_target_html in args.targets)
|
|
print_setting("build_latex", cfg_target_latex in args.targets)
|
|
print_setting("build_intermediate", cfg_target_intermediate in args.targets)
|
|
print_setting("clean_all", cfg_target_clean_all in args.targets)
|
|
print_setting("clean_intermediate", cfg_target_clean_intermediate in args.targets)
|
|
print_setting("clean_html", cfg_target_clean_html in args.targets)
|
|
print_setting("clean_latex", cfg_target_clean_latex in args.targets)
|
|
# Options
|
|
print_setting("skip_api", args.skip_api)
|
|
print_setting("fresh_sphinx_env", args.fresh_sphinx_env)
|
|
|
|
if and_exit:
|
|
exit(0)
|
|
|
|
# ---------------------------------------------------------------------
|
|
# Process command-line args.
|
|
# ---------------------------------------------------------------------
|
|
ap = argparse.ArgumentParser(description='Build LVGL documents')
|
|
ap.add_argument('targets', nargs='+', choices=[
|
|
cfg_target_html,
|
|
cfg_target_latex,
|
|
cfg_target_intermediate,
|
|
cfg_target_clean_all,
|
|
cfg_target_clean_intermediate,
|
|
cfg_target_clean_html,
|
|
cfg_target_clean_latex,
|
|
],
|
|
help='output targets to generate; one or more of these;\n`clean...` targets are completed first')
|
|
|
|
ap.add_argument('-s', '--skip-api' , action='store_true', dest='skip_api',
|
|
help='skip API-page generation' )
|
|
ap.add_argument('-E', '--fresh-env', action='store_true', dest='fresh_sphinx_env',
|
|
help='rebuild Sphinx environment' )
|
|
args = ap.parse_args()
|
|
|
|
if cfg_target_clean_all in args.targets:
|
|
if cfg_target_clean_intermediate not in args.targets:
|
|
args.targets.append(cfg_target_clean_intermediate)
|
|
if cfg_target_clean_html not in args.targets:
|
|
args.targets.append(cfg_target_clean_html)
|
|
if cfg_target_clean_latex not in args.targets:
|
|
args.targets.append(cfg_target_clean_latex)
|
|
|
|
# '-E' option forces Sphinx to rebuild its environment so all docs are
|
|
# fully regenerated, even if not changed.
|
|
# Note: Sphinx runs in ./docs/, but uses `intermediate_dir` for input.
|
|
if args.fresh_sphinx_env:
|
|
announce(__file__, "Force-regenerating all files...")
|
|
env_opt = '-E'
|
|
else:
|
|
env_opt = ''
|
|
|
|
# ---------------------------------------------------------------------
|
|
# Start.
|
|
# ---------------------------------------------------------------------
|
|
t0 = datetime.now()
|
|
|
|
# ---------------------------------------------------------------------
|
|
# Set up paths.
|
|
#
|
|
# Variable Suffixes:
|
|
# _filename = filename without path
|
|
# _path = path leading to a file or directory (absolute or relative)
|
|
# _file = path leading to a file (absolute or relative)
|
|
# _dir = path leading to a directory (absolute or relative)
|
|
# ---------------------------------------------------------------------
|
|
base_dir = os.path.abspath(os.path.dirname(__file__))
|
|
project_dir = os.path.abspath(os.path.join(base_dir, cfg_project_dir))
|
|
examples_dir = os.path.join(project_dir, cfg_examples_dir)
|
|
lvgl_src_dir = os.path.join(project_dir, cfg_lvgl_src_dir)
|
|
|
|
# Establish intermediate directory. The presence of environment variable
|
|
# `LVGL_DOC_BUILD_INTERMEDIATE_DIR` overrides default in `cfg_default_intermediate_dir`.
|
|
if 'LVGL_DOC_BUILD_INTERMEDIATE_DIR' in os.environ:
|
|
intermediate_dir = os.environ['LVGL_DOC_BUILD_INTERMEDIATE_DIR']
|
|
else:
|
|
intermediate_dir = os.path.join(base_dir, cfg_default_intermediate_dir)
|
|
|
|
lv_conf_file = os.path.join(intermediate_dir, cfg_lv_conf_filename)
|
|
lv_temp_conf_file_for_doxygen = os.path.join(lvgl_src_dir, cfg_lv_conf_filename)
|
|
version_dst_file = os.path.join(intermediate_dir, cfg_lv_version_filename)
|
|
top_index_file = os.path.join(intermediate_dir, cfg_top_index_filename)
|
|
doxyfile_src_file = os.path.join(base_dir, cfg_doxyfile_filename)
|
|
doxyfile_dst_file = os.path.join(intermediate_dir, cfg_doxyfile_filename)
|
|
pdf_intermediate_dst_dir = os.path.join(intermediate_dir, cfg_static_dir, cfg_downloads_dir)
|
|
pdf_intermediate_dst_file = os.path.join(pdf_intermediate_dst_dir, cfg_pdf_filename)
|
|
sphinx_path_sep = '/'
|
|
pdf_relative_file = cfg_static_dir + sphinx_path_sep + cfg_downloads_dir + sphinx_path_sep + cfg_pdf_filename
|
|
pdf_link_ref_str = f'PDF Version: :download:`{cfg_pdf_filename} <{pdf_relative_file}>`'
|
|
|
|
# Establish build directory. The presence of environment variable
|
|
# `LVGL_DOC_BUILD_OUTPUT_DIR` overrides default in `cfg_default_output_dir`.
|
|
if 'LVGL_DOC_BUILD_OUTPUT_DIR' in os.environ:
|
|
output_dir = os.environ['LVGL_DOC_BUILD_OUTPUT_DIR']
|
|
else:
|
|
output_dir = os.path.join(base_dir, cfg_default_output_dir)
|
|
|
|
html_output_dir = os.path.join(output_dir, 'html')
|
|
latex_output_dir = os.path.join(output_dir, 'latex')
|
|
pdf_output_dir = os.path.join(output_dir, 'pdf')
|
|
pdf_src_file = os.path.join(latex_output_dir, cfg_pdf_filename)
|
|
pdf_dst_file = os.path.join(pdf_output_dir, cfg_pdf_filename)
|
|
version_src_file = os.path.join(project_dir, cfg_lv_version_filename)
|
|
|
|
# Special stuff for right-aligning PDF download link.
|
|
# Note: this needs to be embedded in a <div> tag because the
|
|
# Sphinx `:download:` role causes the link to appear in a <p> tag
|
|
# and in HTML5, <p> tags cannot be nested!
|
|
cfg_right_just_para_text = """.. raw:: html
|
|
|
|
<div style="text-align: right;">"""
|
|
cfg_end_right_just_para_text = """.. raw:: html
|
|
|
|
</div>"""
|
|
# Blank lines are required due to the directives.
|
|
cfg_pdf_link_ref_block_str = \
|
|
cfg_right_just_para_text + os.linesep \
|
|
+ os.linesep \
|
|
+ pdf_link_ref_str + os.linesep + \
|
|
os.linesep \
|
|
+ cfg_end_right_just_para_text + os.linesep \
|
|
+ os.linesep
|
|
|
|
# ---------------------------------------------------------------------
|
|
# Change to script directory for consistent run-time environment.
|
|
# ---------------------------------------------------------------------
|
|
os.chdir(base_dir)
|
|
announce(__file__, f'Intermediate dir: [{intermediate_dir}]')
|
|
announce(__file__, f'Output dir : [{output_dir}]')
|
|
announce(__file__, f'Running from : [{base_dir}]')
|
|
|
|
# ---------------------------------------------------------------------
|
|
# Clean? If so, clean (like `make clean`), but do not exit.
|
|
# ---------------------------------------------------------------------
|
|
some_cleaning_to_be_done = cfg_target_clean_intermediate in args.targets \
|
|
or cfg_target_clean_html in args.targets \
|
|
or cfg_target_clean_latex in args.targets \
|
|
or cfg_target_clean_all in args.targets \
|
|
or ( \
|
|
os.path.isdir(intermediate_dir) \
|
|
and cfg_target_intermediate in args.targets \
|
|
)
|
|
|
|
if some_cleaning_to_be_done:
|
|
announce(__file__, "Cleaning...", box=True)
|
|
|
|
if cfg_target_clean_intermediate in args.targets:
|
|
remove_dir(intermediate_dir)
|
|
|
|
if cfg_target_clean_html in args.targets:
|
|
remove_dir(html_output_dir)
|
|
|
|
if cfg_target_clean_latex in args.targets:
|
|
remove_dir(latex_output_dir)
|
|
|
|
if cfg_target_clean_all in args.targets:
|
|
remove_dir(output_dir)
|
|
|
|
if os.path.isdir(intermediate_dir) and cfg_target_intermediate in args.targets:
|
|
remove_dir(intermediate_dir)
|
|
|
|
# ---------------------------------------------------------------------
|
|
# Populate LVGL_URLPATH and LVGL_GITCOMMIT environment variables:
|
|
# - LVGL_URLPATH <= 'master' or '8.4' '9.2' etc.
|
|
# - LVGL_GITCOMMIT <= same (see 03-Oct-2024 note below).
|
|
#
|
|
# These supply input later in the doc-generation process as follows:
|
|
#
|
|
# LVGL_URLPATH is used by:
|
|
# - `conf.py` to build `html_baseurl` for Sphinx for
|
|
# - generated index
|
|
# - generated search window
|
|
# - establishing canonical page for search engines
|
|
# - `link_roles.py` to generate translation links
|
|
# - `doxygen_xml.py` to generate links to API pages
|
|
#
|
|
# LVGL_GITCOMMIT is used by:
|
|
# - `conf.py` => html_context['github_version'] for
|
|
# Sphinx Read-the-Docs theme to add to [Edit on GitHub] links
|
|
# - `conf.py` => repo_commit_hash for generated EXAMPLES pages for:
|
|
# - [View on GitHub] buttons (view C code examples)
|
|
# - [View on GitHub] buttons (view Python code examples)
|
|
# ---------------------------------------------------------------------
|
|
# 03-Oct-2024: Gabor requested LVGL_GITCOMMIT be changed to a branch
|
|
# name since that will always be current, and it will fix a large
|
|
# number of broken links on the docs website, since commits that
|
|
# generated docs can sometimes go away. This gets used in:
|
|
# - [Edit on GitHub] links in doc pages (via Sphinx theme), and
|
|
# - [View on GitHub] links in example pages (via `example_list.py`
|
|
# and `lv_example.py`).
|
|
# Original code:
|
|
# status, br = subprocess.getstatusoutput("git branch --show-current")
|
|
# _, gitcommit = subprocess.getstatusoutput("git rev-parse HEAD")
|
|
# br = re.sub(r'\* ', '', br)
|
|
# 're' was previously used to remove leading '* ' from current branch
|
|
# string when we were parsing output from bare `git branch` output.
|
|
# This is no longer needed with `--show-current` option now used.
|
|
# ---------------------------------------------------------------------
|
|
status, branch = subprocess.getstatusoutput("git branch --show-current")
|
|
|
|
# If above failed (i.e. `branch` not valid), default to 'master'.
|
|
if status != 0:
|
|
branch = cfg_default_branch
|
|
elif branch == cfg_default_branch:
|
|
# Expected in most cases. Nothing to change.
|
|
pass
|
|
else:
|
|
# `branch` is valid. Capture release version if in a 'release/' branch.
|
|
if branch.startswith('release/'):
|
|
branch = branch[8:]
|
|
else:
|
|
# Default to 'master'.
|
|
branch = cfg_default_branch
|
|
|
|
os.environ['LVGL_URLPATH'] = branch
|
|
os.environ['LVGL_GITCOMMIT'] = branch
|
|
|
|
# ---------------------------------------------------------------------
|
|
# Prep `intermediate_dir` to become the `sphinx-build` source dir.
|
|
# ---------------------------------------------------------------------
|
|
# dirsync `exclude_list` = list of regex patterns to exclude.
|
|
intermediate_re = r'^' + cfg_default_intermediate_dir + r'.*'
|
|
output_re = r'^' + cfg_default_output_dir + r'.*'
|
|
exclude_list = [r'lv_conf\.h', r'^__pycache__.*', intermediate_re, output_re]
|
|
|
|
if intermediate_dir_contents_exists(intermediate_dir):
|
|
# We are just doing an update of the intermediate_dir contents.
|
|
announce(__file__, "Updating intermediate directory...", box=True)
|
|
|
|
exclude_list.append(r'examples.*')
|
|
options = {
|
|
'verbose': True, # Report files copied.
|
|
'create': True, # Create directories if they don't exist.
|
|
'twoway': False, # False means data flow only src => tgt.
|
|
'purge': False, # False means DO NOT remove orphan files/dirs in tgt dir (preserving examples/ dir).
|
|
'exclude': exclude_list
|
|
}
|
|
# action == 'sync' means copy files even when they do not already exist in tgt dir.
|
|
# action == 'update' means DO NOT copy files when they do not already exist in tgt dir.
|
|
dirsync.sync(cfg_doc_src_dir, intermediate_dir, 'sync', **options)
|
|
dirsync.sync(examples_dir, os.path.join(intermediate_dir, cfg_examples_dir), 'sync', **options)
|
|
elif cfg_target_intermediate in args.targets or cfg_target_html in args.targets or cfg_target_latex in args.targets:
|
|
# We are having to create the intermediate_dir contents by copying.
|
|
announce(__file__, "Building intermediate directory...", box=True)
|
|
|
|
t1 = datetime.now()
|
|
copy_method = 1
|
|
|
|
# Both of these methods work.
|
|
if copy_method == 0:
|
|
# --------- Method 0:
|
|
ignore_func = shutil.ignore_patterns('tmp*', 'output*')
|
|
announce(__file__, 'Copying docs...')
|
|
shutil.copytree(cfg_doc_src_dir, intermediate_dir, ignore=ignore_func, dirs_exist_ok=True)
|
|
announce(__file__, 'Copying examples...')
|
|
shutil.copytree(examples_dir, os.path.join(intermediate_dir, cfg_examples_dir), dirs_exist_ok=True)
|
|
else:
|
|
# --------- Method 1:
|
|
options = {
|
|
'create': True, # Create directories if they don't exist.
|
|
'exclude': exclude_list
|
|
}
|
|
# action == 'sync' means copy files even when they do not already exist in tgt dir.
|
|
# action == 'update' means DO NOT copy files when they do not already exist in tgt dir.
|
|
announce(__file__, 'Copying docs...')
|
|
dirsync.sync(cfg_doc_src_dir, intermediate_dir, 'sync', **options)
|
|
announce(__file__, 'Copying examples...')
|
|
dirsync.sync(examples_dir, os.path.join(intermediate_dir, cfg_examples_dir), 'sync', **options)
|
|
|
|
# -----------------------------------------------------------------
|
|
# Build <intermediate_dir>/lv_conf.h from lv_conf_template.h.
|
|
# -----------------------------------------------------------------
|
|
config_builder.run(lv_conf_file)
|
|
# Build a temporary version of this file in ../src/ so Doxygen can see it.
|
|
# Reason: Doxygen's input is the master `lvgl/src/` directory,
|
|
# not the intermediate directory. This file gets deleted later.
|
|
config_builder.run(lv_temp_conf_file_for_doxygen)
|
|
|
|
# -----------------------------------------------------------------
|
|
# Copy `lv_version.h` into intermediate directory.
|
|
# -----------------------------------------------------------------
|
|
shutil.copyfile(version_src_file, version_dst_file)
|
|
|
|
# -----------------------------------------------------------------
|
|
# Generate examples pages. Include sub-pages pages that get included
|
|
# in individual documents where applicable.
|
|
# -----------------------------------------------------------------
|
|
announce(__file__, "Generating examples...")
|
|
example_list.make_warnings_into_errors()
|
|
example_list.exec(intermediate_dir)
|
|
|
|
# -----------------------------------------------------------------
|
|
# Add translation links.
|
|
# This is being skipped in favor of a manually-placed
|
|
# translation link at the top of `./docs/index.rst`.
|
|
# -----------------------------------------------------------------
|
|
# Original code:
|
|
# if True:
|
|
# announce(__file__, "Skipping adding translation links.")
|
|
# else:
|
|
# announce(__file__, "Adding translation links...")
|
|
# add_translation.exec(intermediate_dir)
|
|
|
|
if args.skip_api:
|
|
announce(__file__, "Skipping API generation as requested.")
|
|
else:
|
|
# -------------------------------------------------------------
|
|
# Generate API pages and links thereto.
|
|
# -------------------------------------------------------------
|
|
announce(__file__, "API page and link processing...")
|
|
api_doc_builder.EMIT_WARNINGS = False
|
|
|
|
# api_doc_builder.run() => doxy_xml_parser.DoxygenXml() now:
|
|
# - preps and runs Doxygen generating XML,
|
|
# - loads generated XML.
|
|
# Then api_doc_builder.run():
|
|
# - creates .RST files for API pages, and
|
|
# - adds API hyperlinks to .RST files in the directories in passed array.
|
|
api_doc_builder.build_api_docs(lvgl_src_dir,
|
|
intermediate_dir,
|
|
doxyfile_src_file,
|
|
'auxiliary-modules',
|
|
'common-widget-features',
|
|
'contributing',
|
|
'debugging',
|
|
'getting_started',
|
|
'guides',
|
|
'integration',
|
|
'introduction',
|
|
'libs',
|
|
'main-modules',
|
|
'widgets',
|
|
'xml',
|
|
)
|
|
|
|
# Now that Doxygen has run, this file is no longer needed.
|
|
# Clean up now rather than later to keep logic clean.
|
|
os.remove(lv_temp_conf_file_for_doxygen)
|
|
|
|
# Note time when this stage completed.
|
|
t2 = datetime.now()
|
|
announce(__file__, 'Example/API run time: ' + str(t2 - t1))
|
|
|
|
# ---------------------------------------------------------------------
|
|
# Build PDF
|
|
# ---------------------------------------------------------------------
|
|
if cfg_target_latex not in args.targets:
|
|
announce(__file__, "Skipping Latex build.")
|
|
else:
|
|
t1 = datetime.now()
|
|
announce(__file__, "Building Latex output...", box=True)
|
|
|
|
# If PDF link is present in top index.rst, remove it so PDF
|
|
# does not have a link to itself.
|
|
with open(top_index_file, 'rb') as f:
|
|
index_data = f.read().decode('utf-8')
|
|
|
|
if pdf_link_ref_str in index_data:
|
|
index_data = index_data.replace(pdf_link_ref_str, '')
|
|
|
|
with open(top_index_file, 'wb') as f:
|
|
f.write(index_data.encode('utf-8'))
|
|
|
|
src = intermediate_dir
|
|
dst = output_dir
|
|
cpu = os.cpu_count()
|
|
|
|
# The -D option correctly replaces (overrides) configuration attribute
|
|
# values in the `conf.py` module. Since `conf.py` now correctly
|
|
# computes its own `version` value, we don't have to override it here
|
|
# with a -D options. If it should need to be used in the future,
|
|
# the value after the '=' MUST NOT have quotation marks around it
|
|
# or it won't work. Correct usage: f'-D version={ver}' .
|
|
cmd_line = f'sphinx-build -M latex "{src}" "{dst}" -j {cpu} --fail-on-warning --keep-going'
|
|
cmd(cmd_line)
|
|
|
|
# Generate PDF.
|
|
announce(__file__, "Building PDF...", box=True)
|
|
cmd_line = 'latexmk -pdf "LVGL.tex"'
|
|
cmd(cmd_line, latex_output_dir, False)
|
|
|
|
# Move resulting PDF to its output directory.
|
|
if not os.path.exists(pdf_output_dir):
|
|
os.makedirs(pdf_output_dir)
|
|
|
|
shutil.move(pdf_src_file, pdf_dst_file)
|
|
t2 = datetime.now()
|
|
announce(__file__, 'PDF : ' + pdf_dst_file)
|
|
announce(__file__, 'Latex gen time: ' + str(t2 - t1))
|
|
|
|
# ---------------------------------------------------------------------
|
|
# Build HTML
|
|
# ---------------------------------------------------------------------
|
|
if cfg_target_html not in args.targets:
|
|
announce(__file__, "Skipping HTML build.")
|
|
else:
|
|
t1 = datetime.now()
|
|
announce(__file__, "Building HTML output...", box=True)
|
|
|
|
# If PDF is present in build directory, copy it to
|
|
# intermediate directory for use by HTML build.
|
|
# (Sphinx copies it to its HTML output, so it ends
|
|
# up on the webserver where it can be downloaded).
|
|
if os.path.isfile(pdf_dst_file):
|
|
# Create _static/download/ directory if needed.
|
|
if not os.path.exists(pdf_intermediate_dst_dir):
|
|
os.makedirs(pdf_intermediate_dst_dir)
|
|
|
|
shutil.copyfile(pdf_dst_file, pdf_intermediate_dst_file)
|
|
|
|
# If PDF is present, ensure there is a link to it in the top
|
|
# index.rst so HTML build will have it.
|
|
# Support both Windows and Linux platforms with `os.linesep`.
|
|
if os.path.isfile(pdf_intermediate_dst_file):
|
|
with open(top_index_file, 'rb') as f:
|
|
index_data = f.read().decode('utf-8')
|
|
|
|
if pdf_link_ref_str not in index_data:
|
|
index_data = cfg_pdf_link_ref_block_str + index_data
|
|
|
|
with open(top_index_file, 'wb') as f:
|
|
f.write(index_data.encode('utf-8'))
|
|
|
|
# Note: While it can be done (e.g. if one needs to set a stop point
|
|
# in Sphinx code for development purposes), it is NOT a good idea to
|
|
# run Sphinx from script as
|
|
# from sphinx.cmd.build import main as sphinx_build
|
|
# sphinx_args = [...]
|
|
# sphinx_build(sphinx_args)
|
|
# because it takes ~10X longer to run than `sphinx_build` executable,
|
|
# literally > 3 hours.
|
|
|
|
ver = lvgl_version(version_src_file)
|
|
src = intermediate_dir
|
|
dst = output_dir
|
|
cpu = os.cpu_count()
|
|
|
|
debugging_breathe = 0
|
|
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}']
|
|
|
|
if len(env_opt) > 0:
|
|
sphinx_args.append(f'{env_opt}')
|
|
|
|
sphinx_build(sphinx_args)
|
|
else:
|
|
# The -D option correctly replaces (overrides) configuration attribute
|
|
# values in the `conf.py` module. Since `conf.py` now correctly
|
|
# computes its own `version` value, we don't have to override it here
|
|
# with a -D options. If it should need to be used in the future,
|
|
# the value after the '=' MUST NOT have quotation marks around it
|
|
# or it won't work. Correct usage: f'-D version={ver}' .
|
|
cmd_line = f'sphinx-build -M html "{src}" "{dst}" -j {cpu} {env_opt} --fail-on-warning --keep-going'
|
|
cmd(cmd_line)
|
|
|
|
t2 = datetime.now()
|
|
announce(__file__, 'HTML gen time : ' + str(t2 - t1))
|
|
|
|
# ---------------------------------------------------------------------
|
|
# Indicate results.
|
|
# ---------------------------------------------------------------------
|
|
t_end = datetime.now()
|
|
announce(__file__, 'Total run time: ' + str(t_end - t0))
|
|
announce(__file__, 'Done.')
|
|
|
|
|
|
if __name__ == '__main__':
|
|
"""Make module importable as well as run-able."""
|
|
run()
|