There are too much magic inside docs Makefile to properly run
sphinx-build. Create an ancillary script that contains all
kernel-related sphinx-build call logic currently at Makefile.
Such script is designed to work both as an standalone command
and as part of a Makefile. As such, it properly handles POSIX
jobserver used by GNU make.
On a side note, there was a line number increase due to the
conversion (ignoring comments) is:
Documentation/Makefile | 131 +++----------
tools/docs/sphinx-build-wrapper | 293 +++++++++++++++++++++++++++++++
2 files changed, 323 insertions(+), 101 deletions(-)
Comments and descriptions adds:
tools/docs/sphinx-build-wrapper | 261 +++++++++++++++++++++++++++++++-
So, about half of the script are comments/descriptions.
This is because some things are more verbosed on Python and because
it requires reading env vars from Makefile. Besides it, this script
has some extra features that don't exist at the Makefile:
- It can be called directly from command line;
- It properly return PDF build errors.
When running the script alone, it will only take handle sphinx-build
targets. On other words, it won't runn make rustdoc after building
htmlfiles, nor it will run the extra check scripts.
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
---
Documentation/Makefile | 129 ++-----
tools/docs/sphinx-build-wrapper | 599 ++++++++++++++++++++++++++++++++
2 files changed, 627 insertions(+), 101 deletions(-)
create mode 100755 tools/docs/sphinx-build-wrapper
diff --git a/Documentation/Makefile b/Documentation/Makefile
index fd6399c79fab..380284026c13 100644
--- a/Documentation/Makefile
+++ b/Documentation/Makefile
@@ -23,21 +23,22 @@ SPHINXOPTS =
SPHINXDIRS = .
DOCS_THEME =
DOCS_CSS =
-_SPHINXDIRS = $(sort $(patsubst $(srctree)/Documentation/%/index.rst,%,$(wildcard $(srctree)/Documentation/*/index.rst)))
SPHINX_CONF = conf.py
PAPER =
BUILDDIR = $(obj)/output
PDFLATEX = xelatex
LATEXOPTS = -interaction=batchmode -no-shell-escape
+PYTHONPYCACHEPREFIX ?= $(abspath $(BUILDDIR)/__pycache__)
+
+# Wrapper for sphinx-build
+
+BUILD_WRAPPER = $(srctree)/tools/docs/sphinx-build-wrapper
+
# For denylisting "variable font" files
# Can be overridden by setting as an env variable
FONTS_CONF_DENY_VF ?= $(HOME)/deny-vf
-ifeq ($(findstring 1, $(KBUILD_VERBOSE)),)
-SPHINXOPTS += "-q"
-endif
-
# User-friendly check for sphinx-build
HAVE_SPHINX := $(shell if which $(SPHINXBUILD) >/dev/null 2>&1; then echo 1; else echo 0; fi)
@@ -51,63 +52,29 @@ ifeq ($(HAVE_SPHINX),0)
else # HAVE_SPHINX
-# User-friendly check for pdflatex and latexmk
-HAVE_PDFLATEX := $(shell if which $(PDFLATEX) >/dev/null 2>&1; then echo 1; else echo 0; fi)
-HAVE_LATEXMK := $(shell if which latexmk >/dev/null 2>&1; then echo 1; else echo 0; fi)
+# Common documentation targets
+infodocs texinfodocs latexdocs epubdocs xmldocs pdfdocs linkcheckdocs:
+ $(Q)@$(srctree)/tools/docs/sphinx-pre-install --version-check
+ +$(Q)$(PYTHON3) $(BUILD_WRAPPER) $@ \
+ --sphinxdirs="$(SPHINXDIRS)" --conf="$(SPHINX_CONF)" \
+ --builddir="$(BUILDDIR)" --deny-vf=$(FONTS_CONF_DENY_VF) \
+ --theme=$(DOCS_THEME) --css=$(DOCS_CSS) --paper=$(PAPER)
-ifeq ($(HAVE_LATEXMK),1)
- PDFLATEX := latexmk -$(PDFLATEX)
-endif #HAVE_LATEXMK
-
-# Internal variables.
-PAPEROPT_a4 = -D latex_elements.papersize=a4paper
-PAPEROPT_letter = -D latex_elements.papersize=letterpaper
-ALLSPHINXOPTS = -D kerneldoc_srctree=$(srctree) -D kerneldoc_bin=$(KERNELDOC)
-ALLSPHINXOPTS += $(PAPEROPT_$(PAPER)) $(SPHINXOPTS)
-ifneq ($(wildcard $(srctree)/.config),)
-ifeq ($(CONFIG_RUST),y)
- # Let Sphinx know we will include rustdoc
- ALLSPHINXOPTS += -t rustdoc
-endif
+# Special handling for pdfdocs
+ifneq ($(shell which $(PDFLATEX) >/dev/null 2>&1; echo $$?),0)
+pdfdocs:
+ $(warning The '$(PDFLATEX)' command was not found. Make sure you have it installed and in PATH to produce PDF output.)
+ @echo " SKIP Sphinx $@ target."
endif
-# the i18n builder cannot share the environment and doctrees with the others
-I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
-
-# commands; the 'cmd' from scripts/Kbuild.include is not *loopable*
-loop_cmd = $(echo-cmd) $(cmd_$(1)) || exit;
-
-# $2 sphinx builder e.g. "html"
-# $3 name of the build subfolder / e.g. "userspace-api/media", used as:
-# * dest folder relative to $(BUILDDIR) and
-# * cache folder relative to $(BUILDDIR)/.doctrees
-# $4 dest subfolder e.g. "man" for man pages at userspace-api/media/man
-# $5 reST source folder relative to $(src),
-# e.g. "userspace-api/media" for the linux-tv book-set at ./Documentation/userspace-api/media
-
-PYTHONPYCACHEPREFIX ?= $(abspath $(BUILDDIR)/__pycache__)
-
-quiet_cmd_sphinx = SPHINX $@ --> file://$(abspath $(BUILDDIR)/$3/$4)
- cmd_sphinx = \
- PYTHONPYCACHEPREFIX="$(PYTHONPYCACHEPREFIX)" \
- BUILDDIR=$(abspath $(BUILDDIR)) SPHINX_CONF=$(abspath $(src)/$5/$(SPHINX_CONF)) \
- $(PYTHON3) $(srctree)/scripts/jobserver-exec \
- $(CONFIG_SHELL) $(srctree)/Documentation/sphinx/parallel-wrapper.sh \
- $(SPHINXBUILD) \
- -b $2 \
- -c $(abspath $(src)) \
- -d $(abspath $(BUILDDIR)/.doctrees/$3) \
- -D version=$(KERNELVERSION) -D release=$(KERNELRELEASE) \
- $(ALLSPHINXOPTS) \
- $(abspath $(src)/$5) \
- $(abspath $(BUILDDIR)/$3/$4) && \
- if [ "x$(DOCS_CSS)" != "x" ]; then \
- cp $(if $(patsubst /%,,$(DOCS_CSS)),$(abspath $(srctree)/$(DOCS_CSS)),$(DOCS_CSS)) $(BUILDDIR)/$3/_static/; \
- fi
+# HTML main logic is identical to other targets. However, if rust is enabled,
+# an extra step at the end is required to generate rustdoc.
htmldocs:
- @$(srctree)/tools/docs/sphinx-pre-install --version-check
- @+$(foreach var,$(SPHINXDIRS),$(call loop_cmd,sphinx,html,$(var),,$(var)))
-
+ $(Q)@$(srctree)/tools/docs/sphinx-pre-install --version-check
+ +$(Q)$(PYTHON3) $(BUILD_WRAPPER) $@ \
+ --sphinxdirs="$(SPHINXDIRS)" --conf="$(SPHINX_CONF)" \
+ --builddir="$(BUILDDIR)" \
+ --theme=$(DOCS_THEME) --css=$(DOCS_CSS) --paper=$(PAPER)
# If Rust support is available and .config exists, add rustdoc generated contents.
# If there are any, the errors from this make rustdoc will be displayed but
# won't stop the execution of htmldocs
@@ -118,49 +85,6 @@ ifeq ($(CONFIG_RUST),y)
endif
endif
-texinfodocs:
- @$(srctree)/tools/docs/sphinx-pre-install --version-check
- @+$(foreach var,$(SPHINXDIRS),$(call loop_cmd,sphinx,texinfo,$(var),texinfo,$(var)))
-
-# Note: the 'info' Make target is generated by sphinx itself when
-# running the texinfodocs target define above.
-infodocs: texinfodocs
- $(MAKE) -C $(BUILDDIR)/texinfo info
-
-linkcheckdocs:
- @$(foreach var,$(SPHINXDIRS),$(call loop_cmd,sphinx,linkcheck,$(var),,$(var)))
-
-latexdocs:
- @$(srctree)/tools/docs/sphinx-pre-install --version-check
- @+$(foreach var,$(SPHINXDIRS),$(call loop_cmd,sphinx,latex,$(var),latex,$(var)))
-
-ifeq ($(HAVE_PDFLATEX),0)
-
-pdfdocs:
- $(warning The '$(PDFLATEX)' command was not found. Make sure you have it installed and in PATH to produce PDF output.)
- @echo " SKIP Sphinx $@ target."
-
-else # HAVE_PDFLATEX
-
-pdfdocs: DENY_VF = XDG_CONFIG_HOME=$(FONTS_CONF_DENY_VF)
-pdfdocs: latexdocs
- @$(srctree)/tools/docs/sphinx-pre-install --version-check
- $(foreach var,$(SPHINXDIRS), \
- $(MAKE) PDFLATEX="$(PDFLATEX)" LATEXOPTS="$(LATEXOPTS)" $(DENY_VF) -C $(BUILDDIR)/$(var)/latex || $(PYTHON3) $(srctree)/tools/docs/check-variable-fonts.py || exit; \
- mkdir -p $(BUILDDIR)/$(var)/pdf; \
- mv $(subst .tex,.pdf,$(wildcard $(BUILDDIR)/$(var)/latex/*.tex)) $(BUILDDIR)/$(var)/pdf/; \
- )
-
-endif # HAVE_PDFLATEX
-
-epubdocs:
- @$(srctree)/tools/docs/sphinx-pre-install --version-check
- @+$(foreach var,$(SPHINXDIRS),$(call loop_cmd,sphinx,epub,$(var),epub,$(var)))
-
-xmldocs:
- @$(srctree)/tools/docs/sphinx-pre-install --version-check
- @+$(foreach var,$(SPHINXDIRS),$(call loop_cmd,sphinx,xml,$(var),xml,$(var)))
-
endif # HAVE_SPHINX
# The following targets are independent of HAVE_SPHINX, and the rules should
@@ -172,6 +96,9 @@ refcheckdocs:
cleandocs:
$(Q)rm -rf $(BUILDDIR)
+# Used only on help
+_SPHINXDIRS = $(sort $(patsubst $(srctree)/Documentation/%/index.rst,%,$(wildcard $(srctree)/Documentation/*/index.rst)))
+
dochelp:
@echo ' Linux kernel internal documentation in different formats from ReST:'
@echo ' htmldocs - HTML'
diff --git a/tools/docs/sphinx-build-wrapper b/tools/docs/sphinx-build-wrapper
new file mode 100755
index 000000000000..c57c732b879c
--- /dev/null
+++ b/tools/docs/sphinx-build-wrapper
@@ -0,0 +1,599 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2025 Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
+#
+# pylint: disable=R0902, R0912, R0913, R0914, R0915, R0917, C0103
+#
+# Converted from docs Makefile and parallel-wrapper.sh, both under
+# GPLv2, copyrighted since 2008 by the following authors:
+#
+# Akira Yokosawa <akiyks@gmail.com>
+# Arnd Bergmann <arnd@arndb.de>
+# Breno Leitao <leitao@debian.org>
+# Carlos Bilbao <carlos.bilbao@amd.com>
+# Dave Young <dyoung@redhat.com>
+# Donald Hunter <donald.hunter@gmail.com>
+# Geert Uytterhoeven <geert+renesas@glider.be>
+# Jani Nikula <jani.nikula@intel.com>
+# Jan Stancek <jstancek@redhat.com>
+# Jonathan Corbet <corbet@lwn.net>
+# Joshua Clayton <stillcompiling@gmail.com>
+# Kees Cook <keescook@chromium.org>
+# Linus Torvalds <torvalds@linux-foundation.org>
+# Magnus Damm <damm+renesas@opensource.se>
+# Masahiro Yamada <masahiroy@kernel.org>
+# Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
+# Maxim Cournoyer <maxim.cournoyer@gmail.com>
+# Peter Foley <pefoley2@pefoley.com>
+# Randy Dunlap <rdunlap@infradead.org>
+# Rob Herring <robh@kernel.org>
+# Shuah Khan <shuahkh@osg.samsung.com>
+# Thorsten Blum <thorsten.blum@toblux.com>
+# Tomas Winkler <tomas.winkler@intel.com>
+
+
+"""
+Sphinx build wrapper that handles Kernel-specific business rules:
+
+- it gets the Kernel build environment vars;
+- it determines what's the best parallelism;
+- it handles SPHINXDIRS
+
+This tool ensures that MIN_PYTHON_VERSION is satisfied. If version is
+below that, it seeks for a new Python version. If found, it re-runs using
+the newer version.
+"""
+
+import argparse
+import os
+import shlex
+import shutil
+import subprocess
+import sys
+
+from lib.python_version import PythonVersion
+from lib.latex_fonts import LatexFontChecker
+
+LIB_DIR = "../../scripts/lib"
+SRC_DIR = os.path.dirname(os.path.realpath(__file__))
+
+sys.path.insert(0, os.path.join(SRC_DIR, LIB_DIR))
+
+from jobserver import JobserverExec # pylint: disable=C0413,C0411,E0401
+
+#
+# Some constants
+#
+MIN_PYTHON_VERSION = PythonVersion("3.7").version
+PAPER = ["", "a4", "letter"]
+
+TARGETS = {
+ "cleandocs": { "builder": "clean" },
+ "linkcheckdocs": { "builder": "linkcheck" },
+ "htmldocs": { "builder": "html" },
+ "epubdocs": { "builder": "epub", "out_dir": "epub" },
+ "texinfodocs": { "builder": "texinfo", "out_dir": "texinfo" },
+ "infodocs": { "builder": "texinfo", "out_dir": "texinfo" },
+ "latexdocs": { "builder": "latex", "out_dir": "latex" },
+ "pdfdocs": { "builder": "latex", "out_dir": "latex" },
+ "xmldocs": { "builder": "xml", "out_dir": "xml" },
+}
+
+
+#
+# SphinxBuilder class
+#
+
+class SphinxBuilder:
+ """
+ Handles a sphinx-build target, adding needed arguments to build
+ with the Kernel.
+ """
+
+ def is_rust_enabled(self):
+ """Check if rust is enabled at .config"""
+ config_path = os.path.join(self.srctree, ".config")
+ if os.path.isfile(config_path):
+ with open(config_path, "r", encoding="utf-8") as f:
+ return "CONFIG_RUST=y" in f.read()
+ return False
+
+ def get_path(self, path, use_cwd=False, abs_path=False):
+ """
+ Ancillary routine to handle patches the right way, as shell does.
+
+ It first expands "~" and "~user". Then, if patch is not absolute,
+ join self.srctree. Finally, if requested, convert to abspath.
+ """
+
+ path = os.path.expanduser(path)
+ if not path.startswith("/"):
+ if use_cwd:
+ base = os.getcwd()
+ else:
+ base = self.srctree
+
+ path = os.path.join(base, path)
+
+ if abs_path:
+ return os.path.abspath(path)
+
+ return path
+
+ def get_sphinx_extra_opts(self, n_jobs):
+ """
+ Get the number of jobs to be used for docs build passed via command
+ line and desired sphinx verbosity.
+
+ The number of jobs can be on different places:
+
+ 1) It can be passed via "-j" argument;
+ 2) The SPHINXOPTS="-j8" env var may have "-j";
+ 3) if called via GNU make, -j specifies the desired number of jobs.
+ with GNU makefile, this number is available via POSIX jobserver;
+ 4) if none of the above is available, it should default to "-jauto",
+ and let sphinx decide the best value.
+ """
+
+ #
+ # SPHINXOPTS env var, if used, contains extra arguments to be used
+ # by sphinx-build time. Among them, it may contain sphinx verbosity
+ # and desired number of parallel jobs.
+ #
+ parser = argparse.ArgumentParser()
+ parser.add_argument('-j', '--jobs', type=int)
+ parser.add_argument('-q', '--quiet', type=int)
+
+ #
+ # Other sphinx-build arguments go as-is, so place them
+ # at self.sphinxopts, using shell parser
+ #
+ sphinxopts = shlex.split(os.environ.get("SPHINXOPTS", ""))
+
+ #
+ # Build a list of sphinx args, honoring verbosity here if specified
+ #
+
+ verbose = self.verbose
+ sphinx_args, self.sphinxopts = parser.parse_known_args(sphinxopts)
+ if sphinx_args.quiet is True:
+ verbose = False
+
+ #
+ # If the user explicitly sets "-j" at command line, use it.
+ # Otherwise, pick it from SPHINXOPTS args
+ #
+ if n_jobs:
+ self.n_jobs = n_jobs
+ elif sphinx_args.jobs:
+ self.n_jobs = sphinx_args.jobs
+ else:
+ self.n_jobs = None
+
+ if not verbose:
+ self.sphinxopts += ["-q"]
+
+ def __init__(self, builddir, verbose=False, n_jobs=None):
+ """Initialize internal variables"""
+ self.verbose = None
+
+ #
+ # Normal variables passed from Kernel's makefile
+ #
+ self.kernelversion = os.environ.get("KERNELVERSION", "unknown")
+ self.kernelrelease = os.environ.get("KERNELRELEASE", "unknown")
+ self.pdflatex = os.environ.get("PDFLATEX", "xelatex")
+ self.latexopts = os.environ.get("LATEXOPTS", "-interaction=batchmode -no-shell-escape")
+
+ if not verbose:
+ verbose = bool(os.environ.get("KBUILD_VERBOSE", "") != "")
+
+ if verbose is not None:
+ self.verbose = verbose
+
+ #
+ # Source tree directory. This needs to be at os.environ, as
+ # Sphinx extensions use it
+ #
+ self.srctree = os.environ.get("srctree")
+ if not self.srctree:
+ self.srctree = "."
+ os.environ["srctree"] = self.srctree
+
+ #
+ # Now that we can expand srctree, get other directories as well
+ #
+ self.sphinxbuild = os.environ.get("SPHINXBUILD", "sphinx-build")
+ self.kerneldoc = self.get_path(os.environ.get("KERNELDOC",
+ "scripts/kernel-doc.py"))
+ self.builddir = self.get_path(builddir, use_cwd=True, abs_path=True)
+
+ self.config_rust = self.is_rust_enabled()
+
+ #
+ # Get directory locations for LaTeX build toolchain
+ #
+ self.pdflatex_cmd = shutil.which(self.pdflatex)
+ self.latexmk_cmd = shutil.which("latexmk")
+
+ self.env = os.environ.copy()
+
+ self.get_sphinx_extra_opts(n_jobs)
+
+ def run_sphinx(self, sphinx_build, build_args, *args, **pwargs):
+ """
+ Executes sphinx-build using current python3 command.
+
+ When calling via GNU make, POSIX jobserver is used to tell how
+ many jobs are still available from a job pool. claim all remaining
+ jobs, as we don't want sphinx-build to run in parallel with other
+ jobs.
+
+ Despite that, the user may actually force a different value than
+ the number of available jobs via command line.
+
+ The "with" logic here is used to ensure that the claimed jobs will
+ be freed once subprocess finishes
+ """
+
+ with JobserverExec() as jobserver:
+ if jobserver.claim:
+ #
+ # when GNU make is used, claim available jobs from jobserver
+ #
+ n_jobs = str(jobserver.claim)
+ else:
+ #
+ # Otherwise, let sphinx decide by default
+ #
+ n_jobs = "auto"
+
+ #
+ # If explicitly requested via command line, override default
+ #
+ if self.n_jobs:
+ n_jobs = str(self.n_jobs)
+
+ cmd = [sys.executable, sphinx_build]
+ cmd += [f"-j{n_jobs}"]
+ cmd += self.sphinxopts
+ cmd += build_args
+
+ if self.verbose:
+ print(" ".join(cmd))
+
+ return subprocess.call(cmd, *args, **pwargs)
+
+ def handle_html(self, css, output_dir):
+ """
+ Extra steps for HTML and epub output.
+
+ For such targets, we need to ensure that CSS will be properly
+ copied to the output _static directory
+ """
+
+ if not css:
+ return
+
+ css = os.path.expanduser(css)
+ if not css.startswith("/"):
+ css = os.path.join(self.srctree, css)
+
+ static_dir = os.path.join(output_dir, "_static")
+ os.makedirs(static_dir, exist_ok=True)
+
+ try:
+ shutil.copy2(css, static_dir)
+ except (OSError, IOError) as e:
+ print(f"Warning: Failed to copy CSS: {e}", file=sys.stderr)
+
+ def handle_pdf(self, output_dirs, deny_vf):
+ """
+ Extra steps for PDF output.
+
+ As PDF is handled via a LaTeX output, after building the .tex file,
+ a new build is needed to create the PDF output from the latex
+ directory.
+ """
+ builds = {}
+ max_len = 0
+
+ #
+ # Since early 2024, Fedora and openSUSE tumbleweed have started
+ # deploying variable-font format of "Noto CJK", causing LaTeX
+ # to break with CJK. Work around it, by denying the variable font
+ # usage during xelatex build by passing the location of a config
+ # file with a deny list.
+ #
+ # See tools/docs/lib/latex_fonts.py for more details.
+ #
+ if deny_vf:
+ deny_vf = os.path.expanduser(deny_vf)
+ if os.path.isdir(deny_vf):
+ self.env["XDG_CONFIG_HOME"] = deny_vf
+
+ for from_dir in output_dirs:
+ pdf_dir = os.path.join(from_dir, "../pdf")
+ os.makedirs(pdf_dir, exist_ok=True)
+
+ if self.latexmk_cmd:
+ latex_cmd = [self.latexmk_cmd, f"-{self.pdflatex}"]
+ else:
+ latex_cmd = [self.pdflatex]
+
+ latex_cmd.extend(shlex.split(self.latexopts))
+
+ tex_suffix = ".tex"
+
+ #
+ # Process each .tex file
+ #
+
+ has_tex = False
+ build_failed = False
+ with os.scandir(from_dir) as it:
+ for entry in it:
+ if not entry.name.endswith(tex_suffix):
+ continue
+
+ name = entry.name[:-len(tex_suffix)]
+ has_tex = True
+
+ #
+ # LaTeX PDF error code is almost useless for us:
+ # any warning makes it non-zero. For kernel doc builds it
+ # always return non-zero even when build succeeds.
+ # So, let's do the best next thing: check if all PDF
+ # files were built. If they're, print a summary and
+ # return 0 at the end of this function
+ #
+ try:
+ subprocess.run(latex_cmd + [entry.path],
+ cwd=from_dir, check=True, env=self.env)
+ except subprocess.CalledProcessError:
+ pass
+
+ pdf_name = name + ".pdf"
+ pdf_from = os.path.join(from_dir, pdf_name)
+ pdf_to = os.path.join(pdf_dir, pdf_name)
+
+ if os.path.exists(pdf_from):
+ os.rename(pdf_from, pdf_to)
+ builds[name] = os.path.relpath(pdf_to, self.builddir)
+ else:
+ builds[name] = "FAILED"
+ build_failed = True
+
+ name = entry.name.removesuffix(".tex")
+ max_len = max(max_len, len(name))
+
+ if not has_tex:
+ name = os.path.basename(from_dir)
+ max_len = max(max_len, len(name))
+ builds[name] = "FAILED (no .tex)"
+ build_failed = True
+
+ msg = "Summary"
+ msg += "\n" + "=" * len(msg)
+ print()
+ print(msg)
+
+ for pdf_name, pdf_file in builds.items():
+ print(f"{pdf_name:<{max_len}}: {pdf_file}")
+
+ print()
+
+ if build_failed:
+ msg = LatexFontChecker().check()
+ if msg:
+ print(msg)
+
+ sys.exit("PDF build failed: not all PDF files were created.")
+ else:
+ print("All PDF files were built.")
+
+ def handle_info(self, output_dirs):
+ """
+ Extra steps for Info output.
+
+ For texinfo generation, an additional make is needed from the
+ texinfo directory.
+ """
+
+ for output_dir in output_dirs:
+ try:
+ subprocess.run(["make", "info"], cwd=output_dir, check=True)
+ except subprocess.CalledProcessError as e:
+ sys.exit(f"Error generating info docs: {e}")
+
+ def cleandocs(self, builder): # pylint: disable=W0613
+ """Remove documentation output directory"""
+ shutil.rmtree(self.builddir, ignore_errors=True)
+
+ def build(self, target, sphinxdirs=None, conf="conf.py",
+ theme=None, css=None, paper=None, deny_vf=None):
+ """
+ Build documentation using Sphinx. This is the core function of this
+ module. It prepares all arguments required by sphinx-build.
+ """
+
+ builder = TARGETS[target]["builder"]
+ out_dir = TARGETS[target].get("out_dir", "")
+
+ #
+ # Cleandocs doesn't require sphinx-build
+ #
+ if target == "cleandocs":
+ self.cleandocs(builder)
+ return
+
+ if theme:
+ os.environ["DOCS_THEME"] = theme
+
+ #
+ # Other targets require sphinx-build, so check if it exists
+ #
+ sphinxbuild = shutil.which(self.sphinxbuild, path=self.env["PATH"])
+ if not sphinxbuild:
+ sys.exit(f"Error: {self.sphinxbuild} not found in PATH.\n")
+
+ if builder == "latex":
+ if not self.pdflatex_cmd and not self.latexmk_cmd:
+ sys.exit("Error: pdflatex or latexmk required for PDF generation")
+
+ docs_dir = os.path.abspath(os.path.join(self.srctree, "Documentation"))
+
+ #
+ # Fill in base arguments for Sphinx build
+ #
+ kerneldoc = self.kerneldoc
+ if kerneldoc.startswith(self.srctree):
+ kerneldoc = os.path.relpath(kerneldoc, self.srctree)
+
+ args = [ "-b", builder, "-c", docs_dir ]
+
+ if builder == "latex":
+ if not paper:
+ paper = PAPER[1]
+
+ args.extend(["-D", f"latex_elements.papersize={paper}paper"])
+
+ if self.config_rust:
+ args.extend(["-t", "rustdoc"])
+
+ if conf:
+ self.env["SPHINX_CONF"] = self.get_path(conf, abs_path=True)
+
+ if not sphinxdirs:
+ sphinxdirs = os.environ.get("SPHINXDIRS", ".")
+
+ #
+ # sphinxdirs can be a list or a whitespace-separated string
+ #
+ sphinxdirs_list = []
+ for sphinxdir in sphinxdirs:
+ if isinstance(sphinxdir, list):
+ sphinxdirs_list += sphinxdir
+ else:
+ sphinxdirs_list += sphinxdir.split()
+
+ #
+ # Step 1: Build each directory in separate.
+ #
+ # This is not the best way of handling it, as cross-references between
+ # them will be broken, but this is what we've been doing since
+ # the beginning.
+ #
+ output_dirs = []
+ for sphinxdir in sphinxdirs_list:
+ src_dir = os.path.join(docs_dir, sphinxdir)
+ doctree_dir = os.path.join(self.builddir, ".doctrees")
+ output_dir = os.path.join(self.builddir, sphinxdir, out_dir)
+
+ #
+ # Make directory names canonical
+ #
+ src_dir = os.path.normpath(src_dir)
+ doctree_dir = os.path.normpath(doctree_dir)
+ output_dir = os.path.normpath(output_dir)
+
+ os.makedirs(doctree_dir, exist_ok=True)
+ os.makedirs(output_dir, exist_ok=True)
+
+ output_dirs.append(output_dir)
+
+ build_args = args + [
+ "-d", doctree_dir,
+ "-D", f"kerneldoc_bin={kerneldoc}",
+ "-D", f"version={self.kernelversion}",
+ "-D", f"release={self.kernelrelease}",
+ "-D", f"kerneldoc_srctree={self.srctree}",
+ src_dir,
+ output_dir,
+ ]
+
+ try:
+ self.run_sphinx(sphinxbuild, build_args, env=self.env)
+ except (OSError, ValueError, subprocess.SubprocessError) as e:
+ sys.exit(f"Build failed: {repr(e)}")
+
+ #
+ # Ensure that each html/epub output will have needed static files
+ #
+ if target in ["htmldocs", "epubdocs"]:
+ self.handle_html(css, output_dir)
+
+ #
+ # Step 2: Some targets (PDF and info) require an extra step once
+ # sphinx-build finishes
+ #
+ if target == "pdfdocs":
+ self.handle_pdf(output_dirs, deny_vf)
+ elif target == "infodocs":
+ self.handle_info(output_dirs)
+
+def jobs_type(value):
+ """
+ Handle valid values for -j. Accepts Sphinx "-jauto", plus a number
+ equal or bigger than one.
+ """
+ if value is None:
+ return None
+
+ if value.lower() == 'auto':
+ return value.lower()
+
+ try:
+ if int(value) >= 1:
+ return value
+
+ raise argparse.ArgumentTypeError(f"Minimum jobs is 1, got {value}")
+ except ValueError:
+ raise argparse.ArgumentTypeError(f"Must be 'auto' or positive integer, got {value}") # pylint: disable=W0707
+
+def main():
+ """
+ Main function. The only mandatory argument is the target. If not
+ specified, the other arguments will use default values if not
+ specified at os.environ.
+ """
+ parser = argparse.ArgumentParser(description="Kernel documentation builder")
+
+ parser.add_argument("target", choices=list(TARGETS.keys()),
+ help="Documentation target to build")
+ parser.add_argument("--sphinxdirs", nargs="+",
+ help="Specific directories to build")
+ parser.add_argument("--conf", default="conf.py",
+ help="Sphinx configuration file")
+ parser.add_argument("--builddir", default="output",
+ help="Sphinx configuration file")
+
+ parser.add_argument("--theme", help="Sphinx theme to use")
+
+ parser.add_argument("--css", help="Custom CSS file for HTML/EPUB")
+
+ parser.add_argument("--paper", choices=PAPER, default=PAPER[0],
+ help="Paper size for LaTeX/PDF output")
+
+ parser.add_argument('--deny-vf',
+ help="Configuration to deny variable fonts on pdf builds")
+
+ parser.add_argument("-v", "--verbose", action='store_true',
+ help="place build in verbose mode")
+
+ parser.add_argument('-j', '--jobs', type=jobs_type,
+ help="Sets number of jobs to use with sphinx-build")
+
+ args = parser.parse_args()
+
+ PythonVersion.check_python(MIN_PYTHON_VERSION)
+
+ builder = SphinxBuilder(builddir=args.builddir,
+ verbose=args.verbose, n_jobs=args.jobs)
+
+ builder.build(args.target, sphinxdirs=args.sphinxdirs, conf=args.conf,
+ theme=args.theme, css=args.css, paper=args.paper,
+ deny_vf=args.deny_vf)
+
+if __name__ == "__main__":
+ main()
--
2.51.0
[+CC: Jani, -CC: rust people and list] Hi, (just got v8, but sending anyway ...) Now that I could actually apply v7 of the series, here is a (not-so-much knee-jerk) reaction to this change. Please see below. On Wed, 17 Sep 2025 14:15:05 +0200, Mauro Carvalho Chehab wrote: > There are too much magic inside docs Makefile to properly run > sphinx-build. Create an ancillary script that contains all > kernel-related sphinx-build call logic currently at Makefile. > > Such script is designed to work both as an standalone command > and as part of a Makefile. As such, it properly handles POSIX > jobserver used by GNU make. > > On a side note, there was a line number increase due to the > conversion (ignoring comments) is: > > Documentation/Makefile | 131 +++---------- > tools/docs/sphinx-build-wrapper | 293 +++++++++++++++++++++++++++++++ > 2 files changed, 323 insertions(+), 101 deletions(-) > > Comments and descriptions adds: > tools/docs/sphinx-build-wrapper | 261 +++++++++++++++++++++++++++++++- > > So, about half of the script are comments/descriptions. > > This is because some things are more verbosed on Python and because > it requires reading env vars from Makefile. Besides it, this script > has some extra features that don't exist at the Makefile: > > - It can be called directly from command line; > - It properly return PDF build errors. > > When running the script alone, it will only take handle sphinx-build > targets. On other words, it won't runn make rustdoc after building > htmlfiles, nor it will run the extra check scripts. > > Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org> > --- > Documentation/Makefile | 129 ++----- > tools/docs/sphinx-build-wrapper | 599 ++++++++++++++++++++++++++++++++ > 2 files changed, 627 insertions(+), 101 deletions(-) > create mode 100755 tools/docs/sphinx-build-wrapper > > diff --git a/Documentation/Makefile b/Documentation/Makefile > index fd6399c79fab..380284026c13 100644 > --- a/Documentation/Makefile > +++ b/Documentation/Makefile [...] > +# Common documentation targets > +infodocs texinfodocs latexdocs epubdocs xmldocs pdfdocs linkcheckdocs: > + $(Q)@$(srctree)/tools/docs/sphinx-pre-install --version-check > + +$(Q)$(PYTHON3) $(BUILD_WRAPPER) $@ \ > + --sphinxdirs="$(SPHINXDIRS)" --conf="$(SPHINX_CONF)" \ > + --builddir="$(BUILDDIR)" --deny-vf=$(FONTS_CONF_DENY_VF) \ > + --theme=$(DOCS_THEME) --css=$(DOCS_CSS) --paper=$(PAPER) > [...] > +# Special handling for pdfdocs > +ifneq ($(shell which $(PDFLATEX) >/dev/null 2>&1; echo $$?),0) > +pdfdocs: > + $(warning The '$(PDFLATEX)' command was not found. Make sure you have it installed and in PATH to produce PDF output.) > + @echo " SKIP Sphinx $@ target." > endif [...] > + $(Q)@$(srctree)/tools/docs/sphinx-pre-install --version-check > + +$(Q)$(PYTHON3) $(BUILD_WRAPPER) $@ \ > + --sphinxdirs="$(SPHINXDIRS)" --conf="$(SPHINX_CONF)" \ > + --builddir="$(BUILDDIR)" \ > + --theme=$(DOCS_THEME) --css=$(DOCS_CSS) --paper=$(PAPER) > # If Rust support is available and .config exists, add rustdoc generated contents. > # If there are any, the errors from this make rustdoc will be displayed but > # won't stop the execution of htmldocs > @@ -118,49 +85,6 @@ ifeq ($(CONFIG_RUST),y) > endif > endif > [...] > - > -latexdocs: > - @$(srctree)/tools/docs/sphinx-pre-install --version-check > - @+$(foreach var,$(SPHINXDIRS),$(call loop_cmd,sphinx,latex,$(var),latex,$(var))) > - > -ifeq ($(HAVE_PDFLATEX),0) > - > -pdfdocs: > - $(warning The '$(PDFLATEX)' command was not found. Make sure you have it installed and in PATH to produce PDF output.) > - @echo " SKIP Sphinx $@ target." > - > -else # HAVE_PDFLATEX > - > -pdfdocs: DENY_VF = XDG_CONFIG_HOME=$(FONTS_CONF_DENY_VF) > -pdfdocs: latexdocs So, this is removing explicit dependency from pdfdocs to latexdocs. Although it is rare, there is a small chance where Sphinx latex builder crashes and can't complete generating all the necessary files (.tex, .sty). In such cases, I want "make pdfdocs" to give up immediately and not to try to run $(PDFLATEX) at all. It looks like "./tools/docs/sphinx-build-wrapper pdfdocs" doesn't give up at the latexdocs stage in such cases with default SPHINXDIRS="." and continues to run $(PDFLATEX) anyway. As a matter of fact, recent linux-next does exhibit such crashes in the latexdocs stage: As of next-20250917, running: make cleandocs: make pdfdocs" will end up in this way: -------------------------------------------------------------- [...] Markup is unsupported in LaTeX! Versions ======== [...] Last Messages ============= filesystems/xfs/xfs-delayed-logging-design filesystems/xfs/xfs-maintainer-entry-profile filesystems/xfs/xfs-self-describing-metadata filesystems/xfs/xfs-online-fsck-design filesystems/zonefs resolving references... processing filesystems.tex: done writing... failed Loaded Extensions ================= [...] Traceback ========= File "/[...]/sphinx-8.2.3/lib/python3.12/site-packages/sphinx/writers/latex.py", line 1152, in visit_table raise UnsupportedError( sphinx.writers.latex.UnsupportedError: filesystems/f2fs:: longtable does not support nesting a table. [...] make[2]: *** [Documentation/Makefile:138: latexdocs] Error 2 make[1]: *** [/home/akira/git/linux/Makefile:1805: pdfdocs] Error 2 make: *** [Makefile:248: __sub-make] Error 2 -------------------------------------------------------------- If you merge your v7 series into linux-next and resolve confilcts properly, running the same: make cleandocs; make pdfdocs will end in this way: Error: Can't build 45 PDF file(s): pdf/RCU.pdf, pdf/admin-guide.pdf, pdf/locking.pdf, pdf/dev-tools.pdf, pdf/process.pdf, pdf/core-api.pdf, pdf/mm.pdf, pdf/virt.pdf, pdf/block.pdf, pdf/sound.pdf, pdf/kernel-hacking.pdf, pdf/networking.pdf, pdf/infiniband.pdf, pdf/leds.pdf, pdf/maintainer.pdf, pdf/crypto.pdf, pdf/fb.pdf, pdf/accounting.pdf, pdf/livepatch.pdf, pdf/nvme.pdf, pdf/hid.pdf, pdf/isdn.pdf, pdf/trace.pdf, pdf/i2c.pdf, pdf/fault-injection.pdf, pdf/netlabel.pdf, pdf/staging.pdf, pdf/timers.pdf, pdf/firmware-guide.pdf, pdf/hwmon.pdf, pdf/scheduler.pdf, pdf/tools.pdf, pdf/watchdog.pdf, pdf/mhi.pdf, pdf/power.pdf, pdf/devicetree.pdf, pdf/arch.pdf, pdf/spi.pdf, pdf/userspace-api.pdf, pdf/translations.pdf, pdf/fpga.pdf, pdf/cpu-freq.pdf, pdf/security.pdf, pdf/bpf.pdf, pdf/pcmcia.pdf Returning back to next-20250917, running with additional options to GNU Make: make clean; make -k -i pdfdocs behaves mostly the same as the one with sphinx-build-wrapper, which ends up like this: ----------------------------------------------------------------------- [...] make[3]: [Makefile:29: virt.pdf] Error 12 (ignored) mv: cannot stat 'Documentation/output/./latex/RCU.pdf': No such file or directory mv: cannot stat 'Documentation/output/./latex/accounting.pdf': No such file or directory mv: cannot stat 'Documentation/output/./latex/admin-guide.pdf': No such file or directory [...] mv: cannot stat 'Documentation/output/./latex/virt.pdf': No such file or directory mv: cannot stat 'Documentation/output/./latex/watchdog.pdf': No such file or directory make[2]: [Documentation/Makefile:151: pdfdocs] Error 1 (ignored) ----------------------------------------------------------------------- As you might have already noticed, GNU Make provides useful and convinenent command options readily availabe. Some of those I often use include: -i, --ignore-errors Ignore all errors in commands executed to remake files. -k, --keep-going Continue as much as possible after an error. While the target that failed, and those that depend on it, cannot be remade, the other dependencies of these targets can be processed all the same. -O[type], --output-sync[=type] When running multiple jobs in parallel with -j, ensure the output of each job is collected together rather than interspersed with output from other jobs. [...] There are abundunt of other helpful options for various situations. Your change of pdfdocs recipe would deprive me of those flexible options of GNU Make. Because passing those options through MAKEFLAGS is availabe in sub-make invocation of $(MAKE) [1]. [1]: https://www.gnu.org/software/make/manual/html_node/Options_002fRecursion.html And it would diverge our scheme of pdfdocs build from that of Sphinx's latex builder's choice. I don't think re-implementing GNU Make's (limited range of) behavior in python can be justified just because Makefile's way of describing recipes look "magic" to your eyes. Honestly speaking, I just trunt the history proven tool much more than your re-implementation. I'm sure I would miss that make--sub-make chain if your sphinx-build-wrapper were employed in pdfdocs target. As I said above, the behavior you wants can be achieved by using a couple of options to GNU Make. (Give or take the "|| exit;" case.) I'm more inclined to continue using existing approach. In summary, this is my suggestion for the development for the v6.19 (not v6.18) merge window. 1) Using sphinx-build-wrapper in (*}docs targets other than pdfdocs is the way to go. 2) pdfdocs should be a different target that depends on latexdocs. 3) Preserve the make--sub-make chain in the pdfdocs recipe. (for easy access to GNU Make's options) 4) sphinx-build-wrapper has own its rights to cover pdfdocs. I don't care how it behaves when it is directly called for pdfdocs. 5) Build summary as the final message from "make pdfdocs" is a good idea and it should be doable in the Makefile recipe in a different manner. 6) The issue Mauro calls "false positive" (the "|| exit;" pattern in Makefile's loop) might be a desired behavior, but it should be possible to add a knob to suppress it in the Makefile. Mauro, how does this "compromize" sound to you? Regards, Akira > - @$(srctree)/tools/docs/sphinx-pre-install --version-check > - $(foreach var,$(SPHINXDIRS), \ > - $(MAKE) PDFLATEX="$(PDFLATEX)" LATEXOPTS="$(LATEXOPTS)" $(DENY_VF) -C $(BUILDDIR)/$(var)/latex || $(PYTHON3) $(srctree)/tools/docs/check-variable-fonts.py || exit; \ > - mkdir -p $(BUILDDIR)/$(var)/pdf; \ > - mv $(subst .tex,.pdf,$(wildcard $(BUILDDIR)/$(var)/latex/*.tex)) $(BUILDDIR)/$(var)/pdf/; \ > - ) > - [...]
Em Thu, 18 Sep 2025 21:07:10 +0900 Akira Yokosawa <akiyks@gmail.com> escreveu: > [+CC: Jani, -CC: rust people and list] > > Hi, (just got v8, but sending anyway ...) > > Now that I could actually apply v7 of the series, here is a > (not-so-much knee-jerk) reaction to this change. > Please see below. > > On Wed, 17 Sep 2025 14:15:05 +0200, Mauro Carvalho Chehab wrote: > > There are too much magic inside docs Makefile to properly run > > sphinx-build. Create an ancillary script that contains all > > kernel-related sphinx-build call logic currently at Makefile. > > > > Such script is designed to work both as an standalone command > > and as part of a Makefile. As such, it properly handles POSIX > > jobserver used by GNU make. > > > > On a side note, there was a line number increase due to the > > conversion (ignoring comments) is: > > > > Documentation/Makefile | 131 +++---------- > > tools/docs/sphinx-build-wrapper | 293 +++++++++++++++++++++++++++++++ > > 2 files changed, 323 insertions(+), 101 deletions(-) > > > > Comments and descriptions adds: > > tools/docs/sphinx-build-wrapper | 261 +++++++++++++++++++++++++++++++- > > > > So, about half of the script are comments/descriptions. > > > > This is because some things are more verbosed on Python and because > > it requires reading env vars from Makefile. Besides it, this script > > has some extra features that don't exist at the Makefile: > > > > - It can be called directly from command line; > > - It properly return PDF build errors. > > > > When running the script alone, it will only take handle sphinx-build > > targets. On other words, it won't runn make rustdoc after building > > htmlfiles, nor it will run the extra check scripts. > > > > Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org> > > --- > > Documentation/Makefile | 129 ++----- > > tools/docs/sphinx-build-wrapper | 599 ++++++++++++++++++++++++++++++++ > > 2 files changed, 627 insertions(+), 101 deletions(-) > > create mode 100755 tools/docs/sphinx-build-wrapper > > > > diff --git a/Documentation/Makefile b/Documentation/Makefile > > index fd6399c79fab..380284026c13 100644 > > --- a/Documentation/Makefile > > +++ b/Documentation/Makefile > [...] > > > +# Common documentation targets > > +infodocs texinfodocs latexdocs epubdocs xmldocs pdfdocs linkcheckdocs: > > + $(Q)@$(srctree)/tools/docs/sphinx-pre-install --version-check > > + +$(Q)$(PYTHON3) $(BUILD_WRAPPER) $@ \ > > + --sphinxdirs="$(SPHINXDIRS)" --conf="$(SPHINX_CONF)" \ > > + --builddir="$(BUILDDIR)" --deny-vf=$(FONTS_CONF_DENY_VF) \ > > + --theme=$(DOCS_THEME) --css=$(DOCS_CSS) --paper=$(PAPER) > > > [...] > > +# Special handling for pdfdocs > > +ifneq ($(shell which $(PDFLATEX) >/dev/null 2>&1; echo $$?),0) > > +pdfdocs: > > + $(warning The '$(PDFLATEX)' command was not found. Make sure you have it installed and in PATH to produce PDF output.) > > + @echo " SKIP Sphinx $@ target." > > endif > [...] > > > + $(Q)@$(srctree)/tools/docs/sphinx-pre-install --version-check > > + +$(Q)$(PYTHON3) $(BUILD_WRAPPER) $@ \ > > + --sphinxdirs="$(SPHINXDIRS)" --conf="$(SPHINX_CONF)" \ > > + --builddir="$(BUILDDIR)" \ > > + --theme=$(DOCS_THEME) --css=$(DOCS_CSS) --paper=$(PAPER) > > # If Rust support is available and .config exists, add rustdoc generated contents. > > # If there are any, the errors from this make rustdoc will be displayed but > > # won't stop the execution of htmldocs > > @@ -118,49 +85,6 @@ ifeq ($(CONFIG_RUST),y) > > endif > > endif > > > [...] > > > - > > -latexdocs: > > - @$(srctree)/tools/docs/sphinx-pre-install --version-check > > - @+$(foreach var,$(SPHINXDIRS),$(call loop_cmd,sphinx,latex,$(var),latex,$(var))) > > - > > -ifeq ($(HAVE_PDFLATEX),0) > > - > > -pdfdocs: > > - $(warning The '$(PDFLATEX)' command was not found. Make sure you have it installed and in PATH to produce PDF output.) > > - @echo " SKIP Sphinx $@ target." > > - > > -else # HAVE_PDFLATEX > > - > > -pdfdocs: DENY_VF = XDG_CONFIG_HOME=$(FONTS_CONF_DENY_VF) > > -pdfdocs: latexdocs > > So, this is removing explicit dependency from pdfdocs to latexdocs. No, this doesn't change it. See, pdfdocs is now a target. The logic inside the wrapper will first build latexdocs with: try: self.run_sphinx(sphinxbuild, build_args, env=self.env) except (OSError, ValueError, subprocess.SubprocessError) as e: sys.exit(f"Build failed: {repr(e)}") ... if target == "pdfdocs": self.handle_pdf(output_dirs, deny_vf) elif target == "infodocs": self.handle_info(output_dirs) e.g if build fails (SubprocessError), it will report it and won't build pdf. > Although it is rare, there is a small chance where Sphinx latex builder > crashes and can't complete generating all the necessary files (.tex, .sty). There is also another possibility, not currently covered: SPHINXDIRS="non_existing_dir" if non_existing_dir is not at latex_documents, this may silently succeed. The new logic detect this as well, producing a warning that sphinx-build didn't produce any LaTeX .tex files. See at handle_pdf() method: # # Handle case where no .tex files were found # if not has_tex: out_name = "LaTeX files" max_len = max(max_len, len(out_name)) builds[out_name] = "FAILED: no .tex files were generated" build_failed = True > > In such cases, I want "make pdfdocs" to give up immediately and not to > try to run $(PDFLATEX) at all. The doc Makefile has this: # Special handling for pdfdocs ifneq ($(shell which $(PDFLATEX) >/dev/null 2>&1; echo $$?),0) pdfdocs: $(warning The '$(PDFLATEX)' command was not found. Make sure you have it installed and in PATH to produce PDF output.) @echo " SKIP Sphinx $@ target." endif endif # HAVE_SPHINX so, in thesis, it should get such condition early. > It looks like "./tools/docs/sphinx-build-wrapper pdfdocs" doesn't give up > at the latexdocs stage in such cases with default SPHINXDIRS="." and > continues to run $(PDFLATEX) anyway. If you run the script by hand and xelatex is not installed, it will build *.tex files, failing only at .pdf output. I don't think this is a problem, although it would be easy to add a logic there to verify if PDFDOCS is there: self.pdflatex = os.environ.get("PDFLATEX", "xelatex") if not os.path.exists(self.pdflatex) or not os.access(self.pdflatex, os.X_OK): sys.exit(f"Error: {self.pdflatex} doesn't exist or it is not executable") But IMHO this is overkill. > As a matter of fact, recent linux-next does exhibit such crashes in the > latexdocs stage: > > As of next-20250917, running: > make cleandocs: make pdfdocs" will end up in this way: > > -------------------------------------------------------------- > [...] > Markup is unsupported in LaTeX! > > Versions > ======== > > [...] > > Last Messages > ============= > > filesystems/xfs/xfs-delayed-logging-design > filesystems/xfs/xfs-maintainer-entry-profile > filesystems/xfs/xfs-self-describing-metadata > filesystems/xfs/xfs-online-fsck-design > filesystems/zonefs > > resolving references... > processing filesystems.tex: done > writing... > failed > > Loaded Extensions > ================= > > [...] > > Traceback > ========= > > File "/[...]/sphinx-8.2.3/lib/python3.12/site-packages/sphinx/writers/latex.py", line 1152, in visit_table > raise UnsupportedError( > sphinx.writers.latex.UnsupportedError: filesystems/f2fs:: longtable does not support nesting a table. Here, xelatex exists and was installed. This sounds to be due to some change at fs2fs.rst file. Probably it is using a nested table, e.g. a Sphinx table with a table inside it. We had to do some changes on media to avoid that, as this indeed breaks PDF generation. > [...] > > make[2]: *** [Documentation/Makefile:138: latexdocs] Error 2 > make[1]: *** [/home/akira/git/linux/Makefile:1805: pdfdocs] Error 2 > make: *** [Makefile:248: __sub-make] Error 2 > -------------------------------------------------------------- > > If you merge your v7 series into linux-next and resolve confilcts properly, > running the same: > > make cleandocs; make pdfdocs > > will end in this way: > > Error: Can't build 45 PDF file(s): pdf/RCU.pdf, pdf/admin-guide.pdf, pdf/locking.pdf, pdf/dev-tools.pdf, pdf/process.pdf, pdf/core-api.pdf, pdf/mm.pdf, pdf/virt.pdf, pdf/block.pdf, pdf/sound.pdf, pdf/kernel-hacking.pdf, pdf/networking.pdf, pdf/infiniband.pdf, pdf/leds.pdf, pdf/maintainer.pdf, pdf/crypto.pdf, pdf/fb.pdf, pdf/accounting.pdf, pdf/livepatch.pdf, pdf/nvme.pdf, pdf/hid.pdf, pdf/isdn.pdf, pdf/trace.pdf, pdf/i2c.pdf, pdf/fault-injection.pdf, pdf/netlabel.pdf, pdf/staging.pdf, pdf/timers.pdf, pdf/firmware-guide.pdf, pdf/hwmon.pdf, pdf/scheduler.pdf, pdf/tools.pdf, pdf/watchdog.pdf, pdf/mhi.pdf, pdf/power.pdf, pdf/devicetree.pdf, pdf/arch.pdf, pdf/spi.pdf, pdf/userspace-api.pdf, pdf/translations.pdf, pdf/fpga.pdf, pdf/cpu-freq.pdf, pdf/security.pdf, pdf/bpf.pdf, pdf/pcmcia.pdf It sounds that subprocess call is missing check=True. This should likely fix it: try: - self.run_sphinx(sphinxbuild, build_args, env=self.env) + self.run_sphinx(sphinxbuild, build_args, env=self.env, check=True) except (OSError, ValueError, subprocess.SubprocessError) as e: sys.exit(f"Build failed: {repr(e)}") without check=True, subprocess.call won't return an exception, but instead will place the return code at the function return logic. > As you might have already noticed, GNU Make provides useful and convinenent > command options readily availabe. Some of those I often use include: ... > There are abundunt of other helpful options for various situations. > > Your change of pdfdocs recipe would deprive me of those flexible options > of GNU Make. ... > Honestly speaking, I just trunt the history proven tool much more than your > re-implementation. I'm sure I would miss that make--sub-make chain if your > sphinx-build-wrapper were employed in pdfdocs target. As explained, the build process we have is very complex, with lots of exceptions and 4 separate scripts. We really need to have a consolidated logic with everything on it and properly documented. That's said, if you need to support your own particular scenario in a way that you want to solve only with Makefile rules, you can easily create your own Makefile.akira file with: pdfdocs: +make -C Documentation latexdocs +make -C Documentation/output/latex pdf and whatever other specific rules you might want, but this doesn't sound a valid reason to keep a very polluted badly documented Makefile, where nobody knows exactly anymore why each part of it are there... Heh, just on the discussions of this changeset, experienced developers including me forgot that: - _SPHINXDIRS is just a helper var for make help; - the "|| exit" is there to bypass latex broken warnings; - the media uapi builds (before -next) were missing a "+"; - there was an extra script called only when pdfdocs fail; - it is hard to notice that make htmldocs actually ends with make rustdoc > As I said above, the behavior you wants can be achieved by using a couple > of options to GNU Make. (Give or take the "|| exit;" case.) > I'm more inclined to continue using existing approach. > > In summary, this is my suggestion for the development for the v6.19 (not v6.18) > merge window. Merging for 6.19 is OK to me. Maybe the best would be if Jon could merge v8 on a separate branch, to be merged after the merge window. We may then send incremental patches against it containing fixes and improvements. Keeping rebasing/resubmitting a /24 patches (and increasing) series every time sounds a waste of everybody's time: if we agree with the general concept, we can submit things incrementally from now on. > 1) Using sphinx-build-wrapper in (*}docs targets other than pdfdocs is the way > to go. > > 2) pdfdocs should be a different target that depends on latexdocs. > > 3) Preserve the make--sub-make chain in the pdfdocs recipe. (for easy access > to GNU Make's options) > > 4) sphinx-build-wrapper has own its rights to cover pdfdocs. I don't care > how it behaves when it is directly called for pdfdocs. I think that splitting latex and pdf on two separate targets is not a good idea for the normal usecase: most of the time, people just want the final target and don't care what intermediate steps were needed to build the output. - Yet, perhaps we may implement either a "pdf-only" target that would skip first step (sphinx-build) going directly to second step. Or maybe implement on a more generic way, with something similar to: DOC_STEP=1|2 or: SPINXBUILD=0|1 To allow select what build step will be used or to skip sphinx-build call. > 5) Build summary as the final message from "make pdfdocs" is a good idea and > it should be doable in the Makefile recipe in a different manner. Right now, summary is shown V=1, but we can add a way to control the output directly. > 6) The issue Mauro calls "false positive" (the "|| exit;" pattern in Makefile's > loop) might be a desired behavior, but it should be possible to add a knob > to suppress it in the Makefile. Not against adding it. IMHO such scenario could be implemented directly as an option to the wrapper when called from command line, instead of yet-another-env var, but I won't object either way. > > Mauro, how does this "compromize" sound to you? Yes. See my comments above. > > Regards, > Akira > > > - @$(srctree)/tools/docs/sphinx-pre-install --version-check > > - $(foreach var,$(SPHINXDIRS), \ > > - $(MAKE) PDFLATEX="$(PDFLATEX)" LATEXOPTS="$(LATEXOPTS)" $(DENY_VF) -C $(BUILDDIR)/$(var)/latex || $(PYTHON3) $(srctree)/tools/docs/check-variable-fonts.py || exit; \ > > - mkdir -p $(BUILDDIR)/$(var)/pdf; \ > > - mv $(subst .tex,.pdf,$(wildcard $(BUILDDIR)/$(var)/latex/*.tex)) $(BUILDDIR)/$(var)/pdf/; \ > > - ) > > - > [...] > Thanks, Mauro
© 2016 - 2025 Red Hat, Inc.