[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <0fa2c8115aa980fb6802614098f407067f536ecd.1752067814.git.mchehab+huawei@kernel.org>
Date: Wed, 9 Jul 2025 15:52:02 +0200
From: Mauro Carvalho Chehab <mchehab+huawei@...nel.org>
To: Linux Doc Mailing List <linux-doc@...r.kernel.org>,
Jonathan Corbet <corbet@....net>
Cc: Mauro Carvalho Chehab <mchehab+huawei@...nel.org>,
"Akira Yokosawa" <akiyks@...il.com>,
Mauro Carvalho Chehab <mchehab@...nel.org>,
linux-kernel@...r.kernel.org
Subject: [PATCH v2 30/39] scripts: sphinx-pre-install: add documentation for the ancillary classes.
While here, rename a parameter to have its usage better
documented.
No functional changes.
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@...nel.org>
---
scripts/sphinx-pre-install.py | 162 +++++++++++++++++++++++++++++-----
1 file changed, 140 insertions(+), 22 deletions(-)
diff --git a/scripts/sphinx-pre-install.py b/scripts/sphinx-pre-install.py
index 593982f350b3..1dc3f19804ab 100755
--- a/scripts/sphinx-pre-install.py
+++ b/scripts/sphinx-pre-install.py
@@ -33,35 +33,62 @@ MIN_PYTHON_VERSION = parse_version("3.7")
class DepManager:
+ """
+ Manage package dependencies. There are three types of dependencies:
- # Internal types of dependencies.
+ - System: dependencies required for docs build;
+ - Python: python dependencies for a native distro Sphinx install;
+ - PDF: dependencies needed by PDF builds.
+
+ Each dependency can be mandatory or optional. Not installing an optional
+ dependency won't break the build, but will cause degradation at the
+ docs output.
+ """
+
+ # Internal types of dependencies. Don't use them outside DepManager class.
_SYS_TYPE = 0
_PHY_TYPE = 1
_PDF_TYPE = 2
- # Let's define keys as a tuple with the type and mandatory/optional.
- # This way, checking for optional or type is easy.
+ # Dependencies visible outside the class.
+ # The keys are tuple with: (type, is_mandatory flag).
+ #
+ # Currently we're not using all optional dep types. Yet, we'll keep all
+ # possible combinations here. They're not many, and that makes easier
+ # if later needed and for the name() method below
SYSTEM_MANDATORY = (_SYS_TYPE, True)
PYTHON_MANDATORY = (_PHY_TYPE, True)
PDF_MANDATORY = (_PDF_TYPE, True)
- # Currently we're not using all optional types, but let's keep all
- # combinations here, as we may end needing them in the future. Also,
- # it allows a name() function that handles all possibilities.
SYSTEM_OPTIONAL = (_SYS_TYPE, False)
PYTHON_OPTIONAL = (_PHY_TYPE, False)
PDF_OPTIONAL = (_PDF_TYPE, True)
def __init__(self, pdf):
+ """
+ Initialize internal vars:
+
+ - missing: missing dependencies list, containing a distro-independent
+ name for a missing dependency and its type.
+ - missing_pkg: ancillary dict containing missing dependencies in
+ distro namespace, organized by type.
+ - need: total number of needed dependencies. Never cleaned.
+ - optional: total number of optional dependencies. Never cleaned.
+ - pdf: PDF support is enabled?
+ """
self.missing = {}
- self.run = {}
+ self.missing_pkg = {}
self.need = 0
self.optional = 0
self.pdf = pdf
@staticmethod
def name(dtype):
+ """
+ Ancillary routine to output a warn/error message reporting
+ missing dependencies.
+ """
if dtype[0] == DepManager._SYS_TYPE:
msg = "build"
elif dtype[0] == DepManager._PHY_TYPE:
@@ -76,16 +103,22 @@ class DepManager:
@staticmethod
def is_optional(dtype):
+ """Ancillary routine to report if a dependency is optional"""
return not dtype[1]
@staticmethod
def is_pdf(dtype):
+ """Ancillary routine to report if a dependency is for PDF generation"""
if (dtype[0] == DepManager._PDF_TYPE):
return True
return False
def add_package(self, package, dtype):
+ """
+ Add a package at the self.missing() dictionary.
+ Doesn't update missing_pkg.
+ """
is_optional = DepManager.is_optional(dtype)
self.missing[package] = dtype
if is_optional:
@@ -94,6 +127,10 @@ class DepManager:
self.need += 1
def del_package(self, package):
+ """
+ Remove a package at the self.missing() dictionary.
+ Doesn't update missing_pkg.
+ """
if package in self.missing:
del self.missing[package]
@@ -104,13 +141,22 @@ class DepManager:
This is an ackward way to have a separate section to recommend
a package after system main dependencies.
- TODO: rework the logic to prevent needing it
+ TODO: rework the logic to prevent needing it.
"""
self.missing = {}
+ self.missing_pkg = {}
def check_missing(self, progs):
- self.run = {}
+ """
+ Update self.missing_pkg, using progs dict to convert from the
+ agnostic package name to distro-specific one.
+
+ Returns an string with the packages to be installed, sorted and
+ with eventual duplicates removed.
+ """
+
+ self.missing_pkg = {}
for prog, dtype in sorted(self.missing.items()):
# At least on some LTS distros like CentOS 7, texlive doesn't
@@ -123,23 +169,26 @@ class DepManager:
self.optional -= 1
continue
- if not dtype in self.run:
- self.run[dtype] = []
+ if not dtype in self.missing_pkg:
+ self.missing_pkg[dtype] = []
- self.run[dtype].append(progs.get(prog, prog))
+ self.missing_pkg[dtype].append(progs.get(prog, prog))
install = []
- for dtype in self.run.keys():
- install += self.run[dtype]
+ for dtype in self.missing_pkg.keys():
+ install += self.missing_pkg[dtype]
return " ".join(sorted(set(install)))
def warn_install(self):
+ """
+ Emit warnings/errors related to missing packages.
+ """
output_msg = ""
- for dtype in sorted(self.run.keys()):
- progs = " ".join(sorted(set(self.run[dtype])))
+ for dtype in sorted(self.missing_pkg.keys()):
+ progs = " ".join(sorted(set(self.missing_pkg[dtype])))
try:
name = DepManager.name(dtype)
@@ -158,6 +207,11 @@ class AncillaryMethods:
@staticmethod
def which(prog):
+ """
+ Our own implementation of which(). We could instead use
+ shutil.which(), but this function is simple enough.
+ Probably faster to use this implementation than to import shutil.
+ """
for path in os.environ.get("PATH", "").split(":"):
full_path = os.path.join(path, prog)
if os.access(full_path, os.X_OK):
@@ -167,6 +221,10 @@ class AncillaryMethods:
@staticmethod
def get_python_version(cmd):
+ """
+ Get python version from a Python binary. As we need to detect if
+ are out there newer python binaries, we can't rely on sys.release here.
+ """
result = SphinxDependencyChecker.run([cmd, "--version"],
capture_output=True, text=True)
@@ -181,7 +239,13 @@ class AncillaryMethods:
@staticmethod
def find_python():
+ """
+ Detect if are out there any python 3.xy version newer than the
+ current one.
+ Note: this routine is limited to up to 2 digits for python3. We
+ may need to update it one day, hopefully on a distant future.
+ """
patterns = [
"python3.[0-9]",
"python3.[0-9][0-9]",
@@ -200,7 +264,10 @@ class AncillaryMethods:
@staticmethod
def check_python():
-
+ """
+ Check if the current python binary satisfies our minimal requirement
+ for Sphinx build. If not, re-run with a newer version if found.
+ """
cur_ver = sys.version_info[:3]
if cur_ver >= MIN_PYTHON_VERSION:
ver = ver_str(cur_ver)
@@ -240,7 +307,10 @@ class AncillaryMethods:
@staticmethod
def run(*args, **kwargs):
- """Excecute a command, hiding its output by default"""
+ """
+ Excecute a command, hiding its output by default.
+ Preserve comatibility with older Python versions.
+ """
capture_output = kwargs.pop('capture_output', False)
@@ -262,8 +332,15 @@ class AncillaryMethods:
return subprocess.run(*args, **kwargs)
class MissingCheckers(AncillaryMethods):
+ """
+ Contains some ancillary checkers for different types of binaries and
+ package managers.
+ """
def __init__(self, args, texlive):
+ """
+ Initialize its internal variables
+ """
self.pdf = args.pdf
self.virtualenv = args.virtualenv
self.version_check = args.version_check
@@ -280,17 +357,20 @@ class MissingCheckers(AncillaryMethods):
self.install = ""
- #
- # Methods to check if a feature exists
- #
-
def check_missing_file(self, files, package, dtype):
+ """
+ Does the file exists? If not, add it to missing dependencies.
+ """
for f in files:
if os.path.exists(f):
return
self.deps.add_package(package, dtype)
def check_program(self, prog, dtype):
+ """
+ Does the program exists and it is at the PATH?
+ If not, add it to missing dependencies.
+ """
found = self.which(prog)
if found:
return found
@@ -300,6 +380,18 @@ class MissingCheckers(AncillaryMethods):
return None
def check_perl_module(self, prog, dtype):
+ """
+ Does perl have a dependency? Is it available?
+ If not, add it to missing dependencies.
+
+ Right now, we still need Perl for doc build, as it is required
+ by some tools called at docs or kernel build time, like:
+
+ scripts/documentation-file-ref-check
+
+ Also, checkpatch is on Perl.
+ """
+
# While testing with lxc download template, one of the
# distros (Oracle) didn't have perl - nor even an option to install
# before installing oraclelinux-release-el9 package.
@@ -318,6 +410,10 @@ class MissingCheckers(AncillaryMethods):
self.deps.add_package(prog, dtype)
def check_python_module(self, module, is_optional=False):
+ """
+ Does a python module exists outside venv? If not, add it to missing
+ dependencies.
+ """
if is_optional:
dtype = DepManager.PYTHON_OPTIONAL
else:
@@ -329,6 +425,9 @@ class MissingCheckers(AncillaryMethods):
self.deps.add_package(module, dtype)
def check_rpm_missing(self, pkgs, dtype):
+ """
+ Does a rpm package exists? If not, add it to missing dependencies.
+ """
for prog in pkgs:
try:
self.run(["rpm", "-q", prog], check=True)
@@ -336,6 +435,9 @@ class MissingCheckers(AncillaryMethods):
self.deps.add_package(prog, dtype)
def check_pacman_missing(self, pkgs, dtype):
+ """
+ Does a pacman package exists? If not, add it to missing dependencies.
+ """
for prog in pkgs:
try:
self.run(["pacman", "-Q", prog], check=True)
@@ -343,6 +445,9 @@ class MissingCheckers(AncillaryMethods):
self.deps.add_package(prog, dtype)
def check_missing_tex(self, is_optional=False):
+ """
+ Does a LaTeX package exists? If not, add it to missing dependencies.
+ """
if is_optional:
dtype = DepManager.PDF_OPTIONAL
else:
@@ -371,6 +476,9 @@ class MissingCheckers(AncillaryMethods):
self.deps.add_package(package, dtype)
def get_sphinx_fname(self):
+ """
+ Gets the binary filename for sphinx-build.
+ """
if "SPHINXBUILD" in os.environ:
return os.environ["SPHINXBUILD"]
@@ -386,6 +494,9 @@ class MissingCheckers(AncillaryMethods):
return ""
def get_sphinx_version(self, cmd):
+ """
+ Gets sphinx-build version.
+ """
try:
result = self.run([cmd, "--version"],
stdout=subprocess.PIPE,
@@ -404,6 +515,9 @@ class MissingCheckers(AncillaryMethods):
return parse_version(match.group(1))
def check_sphinx(self, conf):
+ """
+ Checks Sphinx minimal requirements
+ """
try:
with open(conf, "r", encoding="utf-8") as f:
for line in f:
@@ -441,6 +555,10 @@ class MissingCheckers(AncillaryMethods):
sys.exit(0)
def catcheck(self, filename):
+ """
+ Reads a file if it exists, returning as string.
+ If not found, returns an empty string.
+ """
if os.path.exists(filename):
with open(filename, "r", encoding="utf-8") as f:
return f.read().strip()
--
2.49.0
Powered by blists - more mailing lists