1819667bcSMauro Carvalho Chehab#!/usr/bin/env python3 2819667bcSMauro Carvalho Chehab# SPDX-License-Identifier: GPL-2.0 3819667bcSMauro Carvalho Chehab# Copyright (C) 2025 Mauro Carvalho Chehab <mchehab+huawei@kernel.org> 4819667bcSMauro Carvalho Chehab# 5819667bcSMauro Carvalho Chehab# pylint: disable=R0902, R0912, R0913, R0914, R0915, R0917, C0103 6819667bcSMauro Carvalho Chehab# 7819667bcSMauro Carvalho Chehab# Converted from docs Makefile and parallel-wrapper.sh, both under 8819667bcSMauro Carvalho Chehab# GPLv2, copyrighted since 2008 by the following authors: 9819667bcSMauro Carvalho Chehab# 10819667bcSMauro Carvalho Chehab# Akira Yokosawa <akiyks@gmail.com> 11819667bcSMauro Carvalho Chehab# Arnd Bergmann <arnd@arndb.de> 12819667bcSMauro Carvalho Chehab# Breno Leitao <leitao@debian.org> 13819667bcSMauro Carvalho Chehab# Carlos Bilbao <carlos.bilbao@amd.com> 14819667bcSMauro Carvalho Chehab# Dave Young <dyoung@redhat.com> 15819667bcSMauro Carvalho Chehab# Donald Hunter <donald.hunter@gmail.com> 16819667bcSMauro Carvalho Chehab# Geert Uytterhoeven <geert+renesas@glider.be> 17819667bcSMauro Carvalho Chehab# Jani Nikula <jani.nikula@intel.com> 18819667bcSMauro Carvalho Chehab# Jan Stancek <jstancek@redhat.com> 19819667bcSMauro Carvalho Chehab# Jonathan Corbet <corbet@lwn.net> 20819667bcSMauro Carvalho Chehab# Joshua Clayton <stillcompiling@gmail.com> 21819667bcSMauro Carvalho Chehab# Kees Cook <keescook@chromium.org> 22819667bcSMauro Carvalho Chehab# Linus Torvalds <torvalds@linux-foundation.org> 23819667bcSMauro Carvalho Chehab# Magnus Damm <damm+renesas@opensource.se> 24819667bcSMauro Carvalho Chehab# Masahiro Yamada <masahiroy@kernel.org> 25819667bcSMauro Carvalho Chehab# Mauro Carvalho Chehab <mchehab+huawei@kernel.org> 26819667bcSMauro Carvalho Chehab# Maxim Cournoyer <maxim.cournoyer@gmail.com> 27819667bcSMauro Carvalho Chehab# Peter Foley <pefoley2@pefoley.com> 28819667bcSMauro Carvalho Chehab# Randy Dunlap <rdunlap@infradead.org> 29819667bcSMauro Carvalho Chehab# Rob Herring <robh@kernel.org> 30819667bcSMauro Carvalho Chehab# Shuah Khan <shuahkh@osg.samsung.com> 31819667bcSMauro Carvalho Chehab# Thorsten Blum <thorsten.blum@toblux.com> 32819667bcSMauro Carvalho Chehab# Tomas Winkler <tomas.winkler@intel.com> 33819667bcSMauro Carvalho Chehab 34819667bcSMauro Carvalho Chehab 35819667bcSMauro Carvalho Chehab""" 36819667bcSMauro Carvalho ChehabSphinx build wrapper that handles Kernel-specific business rules: 37819667bcSMauro Carvalho Chehab 38819667bcSMauro Carvalho Chehab- it gets the Kernel build environment vars; 39819667bcSMauro Carvalho Chehab- it determines what's the best parallelism; 40819667bcSMauro Carvalho Chehab- it handles SPHINXDIRS 41819667bcSMauro Carvalho Chehab 42819667bcSMauro Carvalho ChehabThis tool ensures that MIN_PYTHON_VERSION is satisfied. If version is 43819667bcSMauro Carvalho Chehabbelow that, it seeks for a new Python version. If found, it re-runs using 44819667bcSMauro Carvalho Chehabthe newer version. 45819667bcSMauro Carvalho Chehab""" 46819667bcSMauro Carvalho Chehab 47819667bcSMauro Carvalho Chehabimport argparse 4882c294d4SMauro Carvalho Chehabimport locale 49819667bcSMauro Carvalho Chehabimport os 507e8a8143SMauro Carvalho Chehabimport re 51819667bcSMauro Carvalho Chehabimport shlex 52819667bcSMauro Carvalho Chehabimport shutil 53819667bcSMauro Carvalho Chehabimport subprocess 54819667bcSMauro Carvalho Chehabimport sys 55819667bcSMauro Carvalho Chehab 5608e14bc1SMauro Carvalho Chehabfrom concurrent import futures 577e8a8143SMauro Carvalho Chehabfrom glob import glob 5808e14bc1SMauro Carvalho Chehab 59819667bcSMauro Carvalho Chehab 60778b8ebeSJonathan CorbetLIB_DIR = "../lib/python" 61819667bcSMauro Carvalho ChehabSRC_DIR = os.path.dirname(os.path.realpath(__file__)) 62819667bcSMauro Carvalho Chehab 63819667bcSMauro Carvalho Chehabsys.path.insert(0, os.path.join(SRC_DIR, LIB_DIR)) 64819667bcSMauro Carvalho Chehab 65992a9df4SJonathan Corbetfrom kdoc.python_version import PythonVersion 66992a9df4SJonathan Corbetfrom kdoc.latex_fonts import LatexFontChecker 67819667bcSMauro Carvalho Chehabfrom jobserver import JobserverExec # pylint: disable=C0413,C0411,E0401 68819667bcSMauro Carvalho Chehab 69819667bcSMauro Carvalho Chehab# 70819667bcSMauro Carvalho Chehab# Some constants 71819667bcSMauro Carvalho Chehab# 7242180adaSMauro Carvalho ChehabVENV_DEFAULT = "sphinx_latest" 73819667bcSMauro Carvalho ChehabMIN_PYTHON_VERSION = PythonVersion("3.7").version 74819667bcSMauro Carvalho ChehabPAPER = ["", "a4", "letter"] 75819667bcSMauro Carvalho Chehab 76819667bcSMauro Carvalho ChehabTARGETS = { 77819667bcSMauro Carvalho Chehab "cleandocs": { "builder": "clean" }, 78819667bcSMauro Carvalho Chehab "linkcheckdocs": { "builder": "linkcheck" }, 79819667bcSMauro Carvalho Chehab "htmldocs": { "builder": "html" }, 80819667bcSMauro Carvalho Chehab "epubdocs": { "builder": "epub", "out_dir": "epub" }, 81819667bcSMauro Carvalho Chehab "texinfodocs": { "builder": "texinfo", "out_dir": "texinfo" }, 82819667bcSMauro Carvalho Chehab "infodocs": { "builder": "texinfo", "out_dir": "texinfo" }, 837e8a8143SMauro Carvalho Chehab "mandocs": { "builder": "man", "out_dir": "man" }, 84819667bcSMauro Carvalho Chehab "latexdocs": { "builder": "latex", "out_dir": "latex" }, 85819667bcSMauro Carvalho Chehab "pdfdocs": { "builder": "latex", "out_dir": "latex" }, 86819667bcSMauro Carvalho Chehab "xmldocs": { "builder": "xml", "out_dir": "xml" }, 87819667bcSMauro Carvalho Chehab} 88819667bcSMauro Carvalho Chehab 89819667bcSMauro Carvalho Chehab 90819667bcSMauro Carvalho Chehab# 91819667bcSMauro Carvalho Chehab# SphinxBuilder class 92819667bcSMauro Carvalho Chehab# 93819667bcSMauro Carvalho Chehab 94819667bcSMauro Carvalho Chehabclass SphinxBuilder: 95819667bcSMauro Carvalho Chehab """ 96819667bcSMauro Carvalho Chehab Handles a sphinx-build target, adding needed arguments to build 97819667bcSMauro Carvalho Chehab with the Kernel. 98819667bcSMauro Carvalho Chehab """ 99819667bcSMauro Carvalho Chehab 100819667bcSMauro Carvalho Chehab def get_path(self, path, use_cwd=False, abs_path=False): 101819667bcSMauro Carvalho Chehab """ 102819667bcSMauro Carvalho Chehab Ancillary routine to handle patches the right way, as shell does. 103819667bcSMauro Carvalho Chehab 104819667bcSMauro Carvalho Chehab It first expands "~" and "~user". Then, if patch is not absolute, 105819667bcSMauro Carvalho Chehab join self.srctree. Finally, if requested, convert to abspath. 106819667bcSMauro Carvalho Chehab """ 107819667bcSMauro Carvalho Chehab 108819667bcSMauro Carvalho Chehab path = os.path.expanduser(path) 109819667bcSMauro Carvalho Chehab if not path.startswith("/"): 110819667bcSMauro Carvalho Chehab if use_cwd: 111819667bcSMauro Carvalho Chehab base = os.getcwd() 112819667bcSMauro Carvalho Chehab else: 113819667bcSMauro Carvalho Chehab base = self.srctree 114819667bcSMauro Carvalho Chehab 115819667bcSMauro Carvalho Chehab path = os.path.join(base, path) 116819667bcSMauro Carvalho Chehab 117819667bcSMauro Carvalho Chehab if abs_path: 118819667bcSMauro Carvalho Chehab return os.path.abspath(path) 119819667bcSMauro Carvalho Chehab 120819667bcSMauro Carvalho Chehab return path 121819667bcSMauro Carvalho Chehab 122ffb569d5SThomas Weißschuh def check_rust(self, sphinxdirs): 123464257baSMauro Carvalho Chehab """ 124464257baSMauro Carvalho Chehab Checks if Rust is enabled 125464257baSMauro Carvalho Chehab """ 126464257baSMauro Carvalho Chehab config = os.path.join(self.srctree, ".config") 127464257baSMauro Carvalho Chehab 128ffb569d5SThomas Weißschuh if not {'.', 'rust'}.intersection(sphinxdirs): 129ffb569d5SThomas Weißschuh return False 130ffb569d5SThomas Weißschuh 131464257baSMauro Carvalho Chehab if not os.path.isfile(config): 1322d652135SThomas Weißschuh return False 133464257baSMauro Carvalho Chehab 134464257baSMauro Carvalho Chehab re_rust = re.compile(r"CONFIG_RUST=(m|y)") 135464257baSMauro Carvalho Chehab 136464257baSMauro Carvalho Chehab try: 137464257baSMauro Carvalho Chehab with open(config, "r", encoding="utf-8") as fp: 138464257baSMauro Carvalho Chehab for line in fp: 139464257baSMauro Carvalho Chehab if re_rust.match(line): 1402d652135SThomas Weißschuh return True 141464257baSMauro Carvalho Chehab 142464257baSMauro Carvalho Chehab except OSError as e: 143464257baSMauro Carvalho Chehab print(f"Failed to open {config}", file=sys.stderr) 1442d652135SThomas Weißschuh return False 1452d652135SThomas Weißschuh 1462d652135SThomas Weißschuh return False 147464257baSMauro Carvalho Chehab 148819667bcSMauro Carvalho Chehab def get_sphinx_extra_opts(self, n_jobs): 149819667bcSMauro Carvalho Chehab """ 150819667bcSMauro Carvalho Chehab Get the number of jobs to be used for docs build passed via command 151819667bcSMauro Carvalho Chehab line and desired sphinx verbosity. 152819667bcSMauro Carvalho Chehab 153819667bcSMauro Carvalho Chehab The number of jobs can be on different places: 154819667bcSMauro Carvalho Chehab 155819667bcSMauro Carvalho Chehab 1) It can be passed via "-j" argument; 156819667bcSMauro Carvalho Chehab 2) The SPHINXOPTS="-j8" env var may have "-j"; 157819667bcSMauro Carvalho Chehab 3) if called via GNU make, -j specifies the desired number of jobs. 158819667bcSMauro Carvalho Chehab with GNU makefile, this number is available via POSIX jobserver; 159819667bcSMauro Carvalho Chehab 4) if none of the above is available, it should default to "-jauto", 160819667bcSMauro Carvalho Chehab and let sphinx decide the best value. 161819667bcSMauro Carvalho Chehab """ 162819667bcSMauro Carvalho Chehab 163819667bcSMauro Carvalho Chehab # 164819667bcSMauro Carvalho Chehab # SPHINXOPTS env var, if used, contains extra arguments to be used 165819667bcSMauro Carvalho Chehab # by sphinx-build time. Among them, it may contain sphinx verbosity 166819667bcSMauro Carvalho Chehab # and desired number of parallel jobs. 167819667bcSMauro Carvalho Chehab # 168819667bcSMauro Carvalho Chehab parser = argparse.ArgumentParser() 169819667bcSMauro Carvalho Chehab parser.add_argument('-j', '--jobs', type=int) 170e123e00aSMauro Carvalho Chehab parser.add_argument('-q', '--quiet', action='store_true') 171b09cc1ddSMauro Carvalho Chehab parser.add_argument('-v', '--verbose', default=0, action='count') 172819667bcSMauro Carvalho Chehab 173819667bcSMauro Carvalho Chehab # 174819667bcSMauro Carvalho Chehab # Other sphinx-build arguments go as-is, so place them 175819667bcSMauro Carvalho Chehab # at self.sphinxopts, using shell parser 176819667bcSMauro Carvalho Chehab # 177819667bcSMauro Carvalho Chehab sphinxopts = shlex.split(os.environ.get("SPHINXOPTS", "")) 178819667bcSMauro Carvalho Chehab 179819667bcSMauro Carvalho Chehab # 180819667bcSMauro Carvalho Chehab # Build a list of sphinx args, honoring verbosity here if specified 181819667bcSMauro Carvalho Chehab # 182819667bcSMauro Carvalho Chehab 183819667bcSMauro Carvalho Chehab sphinx_args, self.sphinxopts = parser.parse_known_args(sphinxopts) 184b09cc1ddSMauro Carvalho Chehab 185b09cc1ddSMauro Carvalho Chehab verbose = sphinx_args.verbose 186b09cc1ddSMauro Carvalho Chehab if self.verbose: 187b09cc1ddSMauro Carvalho Chehab verbose += 1 188b09cc1ddSMauro Carvalho Chehab 189819667bcSMauro Carvalho Chehab if sphinx_args.quiet is True: 190b09cc1ddSMauro Carvalho Chehab verbose = 0 191819667bcSMauro Carvalho Chehab 192819667bcSMauro Carvalho Chehab # 193819667bcSMauro Carvalho Chehab # If the user explicitly sets "-j" at command line, use it. 194819667bcSMauro Carvalho Chehab # Otherwise, pick it from SPHINXOPTS args 195819667bcSMauro Carvalho Chehab # 196819667bcSMauro Carvalho Chehab if n_jobs: 197819667bcSMauro Carvalho Chehab self.n_jobs = n_jobs 198819667bcSMauro Carvalho Chehab elif sphinx_args.jobs: 199819667bcSMauro Carvalho Chehab self.n_jobs = sphinx_args.jobs 200819667bcSMauro Carvalho Chehab else: 201819667bcSMauro Carvalho Chehab self.n_jobs = None 202819667bcSMauro Carvalho Chehab 203b09cc1ddSMauro Carvalho Chehab if verbose < 1: 204819667bcSMauro Carvalho Chehab self.sphinxopts += ["-q"] 205b09cc1ddSMauro Carvalho Chehab else: 206b09cc1ddSMauro Carvalho Chehab for i in range(1, sphinx_args.verbose): 207b09cc1ddSMauro Carvalho Chehab self.sphinxopts += ["-v"] 208819667bcSMauro Carvalho Chehab 20942180adaSMauro Carvalho Chehab def __init__(self, builddir, venv=None, verbose=False, n_jobs=None, 21042180adaSMauro Carvalho Chehab interactive=None): 211819667bcSMauro Carvalho Chehab """Initialize internal variables""" 21242180adaSMauro Carvalho Chehab self.venv = venv 213819667bcSMauro Carvalho Chehab self.verbose = None 214819667bcSMauro Carvalho Chehab 215819667bcSMauro Carvalho Chehab # 216819667bcSMauro Carvalho Chehab # Normal variables passed from Kernel's makefile 217819667bcSMauro Carvalho Chehab # 218819667bcSMauro Carvalho Chehab self.kernelversion = os.environ.get("KERNELVERSION", "unknown") 219819667bcSMauro Carvalho Chehab self.kernelrelease = os.environ.get("KERNELRELEASE", "unknown") 220819667bcSMauro Carvalho Chehab self.pdflatex = os.environ.get("PDFLATEX", "xelatex") 2212f99b85eSMauro Carvalho Chehab 22235b9d338SMauro Carvalho Chehab # 22335b9d338SMauro Carvalho Chehab # Kernel main Makefile defines a PYTHON3 variable whose default is 22435b9d338SMauro Carvalho Chehab # "python3". When set to a different value, it allows running a 22535b9d338SMauro Carvalho Chehab # diferent version than the default official python3 package. 22635b9d338SMauro Carvalho Chehab # Several distros package python3xx-sphinx packages with newer 22735b9d338SMauro Carvalho Chehab # versions of Python and sphinx-build. 22835b9d338SMauro Carvalho Chehab # 22935b9d338SMauro Carvalho Chehab # Honor such variable different than default 23035b9d338SMauro Carvalho Chehab # 23135b9d338SMauro Carvalho Chehab self.python = os.environ.get("PYTHON3") 23235b9d338SMauro Carvalho Chehab if self.python == "python3": 23335b9d338SMauro Carvalho Chehab self.python = None 23435b9d338SMauro Carvalho Chehab 2352f99b85eSMauro Carvalho Chehab if not interactive: 236819667bcSMauro Carvalho Chehab self.latexopts = os.environ.get("LATEXOPTS", "-interaction=batchmode -no-shell-escape") 2372f99b85eSMauro Carvalho Chehab else: 2382f99b85eSMauro Carvalho Chehab self.latexopts = os.environ.get("LATEXOPTS", "") 239819667bcSMauro Carvalho Chehab 240819667bcSMauro Carvalho Chehab if not verbose: 241819667bcSMauro Carvalho Chehab verbose = bool(os.environ.get("KBUILD_VERBOSE", "") != "") 242819667bcSMauro Carvalho Chehab 243819667bcSMauro Carvalho Chehab if verbose is not None: 244819667bcSMauro Carvalho Chehab self.verbose = verbose 245819667bcSMauro Carvalho Chehab 246819667bcSMauro Carvalho Chehab # 247819667bcSMauro Carvalho Chehab # Source tree directory. This needs to be at os.environ, as 248819667bcSMauro Carvalho Chehab # Sphinx extensions use it 249819667bcSMauro Carvalho Chehab # 250819667bcSMauro Carvalho Chehab self.srctree = os.environ.get("srctree") 251819667bcSMauro Carvalho Chehab if not self.srctree: 252819667bcSMauro Carvalho Chehab self.srctree = "." 253819667bcSMauro Carvalho Chehab os.environ["srctree"] = self.srctree 254819667bcSMauro Carvalho Chehab 255819667bcSMauro Carvalho Chehab # 256819667bcSMauro Carvalho Chehab # Now that we can expand srctree, get other directories as well 257819667bcSMauro Carvalho Chehab # 258819667bcSMauro Carvalho Chehab self.sphinxbuild = os.environ.get("SPHINXBUILD", "sphinx-build") 259819667bcSMauro Carvalho Chehab self.kerneldoc = self.get_path(os.environ.get("KERNELDOC", 260eba6ffd1SJonathan Corbet "tools/docs/kernel-doc")) 261819667bcSMauro Carvalho Chehab self.builddir = self.get_path(builddir, use_cwd=True, abs_path=True) 262819667bcSMauro Carvalho Chehab 263819667bcSMauro Carvalho Chehab # 264819667bcSMauro Carvalho Chehab # Get directory locations for LaTeX build toolchain 265819667bcSMauro Carvalho Chehab # 266819667bcSMauro Carvalho Chehab self.pdflatex_cmd = shutil.which(self.pdflatex) 267819667bcSMauro Carvalho Chehab self.latexmk_cmd = shutil.which("latexmk") 268819667bcSMauro Carvalho Chehab 269819667bcSMauro Carvalho Chehab self.env = os.environ.copy() 270819667bcSMauro Carvalho Chehab 271819667bcSMauro Carvalho Chehab self.get_sphinx_extra_opts(n_jobs) 272819667bcSMauro Carvalho Chehab 27342180adaSMauro Carvalho Chehab # 27442180adaSMauro Carvalho Chehab # If venv command line argument is specified, run Sphinx from venv 27542180adaSMauro Carvalho Chehab # 27642180adaSMauro Carvalho Chehab if venv: 27742180adaSMauro Carvalho Chehab bin_dir = os.path.join(venv, "bin") 27842180adaSMauro Carvalho Chehab if not os.path.isfile(os.path.join(bin_dir, "activate")): 27942180adaSMauro Carvalho Chehab sys.exit(f"Venv {venv} not found.") 28042180adaSMauro Carvalho Chehab 28142180adaSMauro Carvalho Chehab # "activate" virtual env 28242180adaSMauro Carvalho Chehab self.env["PATH"] = bin_dir + ":" + self.env["PATH"] 28342180adaSMauro Carvalho Chehab self.env["VIRTUAL_ENV"] = venv 28442180adaSMauro Carvalho Chehab if "PYTHONHOME" in self.env: 28542180adaSMauro Carvalho Chehab del self.env["PYTHONHOME"] 28642180adaSMauro Carvalho Chehab print(f"Setting venv to {venv}") 28742180adaSMauro Carvalho Chehab 288819667bcSMauro Carvalho Chehab def run_sphinx(self, sphinx_build, build_args, *args, **pwargs): 289819667bcSMauro Carvalho Chehab """ 290819667bcSMauro Carvalho Chehab Executes sphinx-build using current python3 command. 291819667bcSMauro Carvalho Chehab 292819667bcSMauro Carvalho Chehab When calling via GNU make, POSIX jobserver is used to tell how 293819667bcSMauro Carvalho Chehab many jobs are still available from a job pool. claim all remaining 294819667bcSMauro Carvalho Chehab jobs, as we don't want sphinx-build to run in parallel with other 295819667bcSMauro Carvalho Chehab jobs. 296819667bcSMauro Carvalho Chehab 297819667bcSMauro Carvalho Chehab Despite that, the user may actually force a different value than 298819667bcSMauro Carvalho Chehab the number of available jobs via command line. 299819667bcSMauro Carvalho Chehab 300819667bcSMauro Carvalho Chehab The "with" logic here is used to ensure that the claimed jobs will 301819667bcSMauro Carvalho Chehab be freed once subprocess finishes 302819667bcSMauro Carvalho Chehab """ 303819667bcSMauro Carvalho Chehab 304819667bcSMauro Carvalho Chehab with JobserverExec() as jobserver: 305819667bcSMauro Carvalho Chehab if jobserver.claim: 306819667bcSMauro Carvalho Chehab # 307819667bcSMauro Carvalho Chehab # when GNU make is used, claim available jobs from jobserver 308819667bcSMauro Carvalho Chehab # 309819667bcSMauro Carvalho Chehab n_jobs = str(jobserver.claim) 310819667bcSMauro Carvalho Chehab else: 311819667bcSMauro Carvalho Chehab # 312819667bcSMauro Carvalho Chehab # Otherwise, let sphinx decide by default 313819667bcSMauro Carvalho Chehab # 314819667bcSMauro Carvalho Chehab n_jobs = "auto" 315819667bcSMauro Carvalho Chehab 316819667bcSMauro Carvalho Chehab # 317819667bcSMauro Carvalho Chehab # If explicitly requested via command line, override default 318819667bcSMauro Carvalho Chehab # 319819667bcSMauro Carvalho Chehab if self.n_jobs: 320819667bcSMauro Carvalho Chehab n_jobs = str(self.n_jobs) 321819667bcSMauro Carvalho Chehab 32235b9d338SMauro Carvalho Chehab # 32335b9d338SMauro Carvalho Chehab # We can't simply call python3 sphinx-build, as OpenSUSE 32435b9d338SMauro Carvalho Chehab # Tumbleweed uses an ELF binary file (/usr/bin/alts) to switch 32535b9d338SMauro Carvalho Chehab # between different versions of sphinx-build. So, only call it 32635b9d338SMauro Carvalho Chehab # prepending "python3.xx" when PYTHON3 variable is not default. 32735b9d338SMauro Carvalho Chehab # 32835b9d338SMauro Carvalho Chehab if self.python: 32935b9d338SMauro Carvalho Chehab cmd = [self.python] 33042180adaSMauro Carvalho Chehab else: 33135b9d338SMauro Carvalho Chehab cmd = [] 33242180adaSMauro Carvalho Chehab 33342180adaSMauro Carvalho Chehab cmd += [sphinx_build] 334819667bcSMauro Carvalho Chehab cmd += [f"-j{n_jobs}"] 335819667bcSMauro Carvalho Chehab cmd += build_args 3361f6e3f21SAkira Yokosawa cmd += self.sphinxopts 337819667bcSMauro Carvalho Chehab 338819667bcSMauro Carvalho Chehab if self.verbose: 339819667bcSMauro Carvalho Chehab print(" ".join(cmd)) 340819667bcSMauro Carvalho Chehab 341819667bcSMauro Carvalho Chehab return subprocess.call(cmd, *args, **pwargs) 342819667bcSMauro Carvalho Chehab 343464257baSMauro Carvalho Chehab def handle_html(self, css, output_dir): 344819667bcSMauro Carvalho Chehab """ 345819667bcSMauro Carvalho Chehab Extra steps for HTML and epub output. 346819667bcSMauro Carvalho Chehab 347819667bcSMauro Carvalho Chehab For such targets, we need to ensure that CSS will be properly 348819667bcSMauro Carvalho Chehab copied to the output _static directory 349819667bcSMauro Carvalho Chehab """ 350819667bcSMauro Carvalho Chehab 3512118ba7dSMauro Carvalho Chehab if css: 352819667bcSMauro Carvalho Chehab css = os.path.expanduser(css) 353819667bcSMauro Carvalho Chehab if not css.startswith("/"): 354819667bcSMauro Carvalho Chehab css = os.path.join(self.srctree, css) 355819667bcSMauro Carvalho Chehab 356819667bcSMauro Carvalho Chehab static_dir = os.path.join(output_dir, "_static") 357819667bcSMauro Carvalho Chehab os.makedirs(static_dir, exist_ok=True) 358819667bcSMauro Carvalho Chehab 359819667bcSMauro Carvalho Chehab try: 360819667bcSMauro Carvalho Chehab shutil.copy2(css, static_dir) 361819667bcSMauro Carvalho Chehab except (OSError, IOError) as e: 362819667bcSMauro Carvalho Chehab print(f"Warning: Failed to copy CSS: {e}", file=sys.stderr) 363819667bcSMauro Carvalho Chehab 36408e14bc1SMauro Carvalho Chehab def build_pdf_file(self, latex_cmd, from_dir, path): 36508e14bc1SMauro Carvalho Chehab """Builds a single pdf file using latex_cmd""" 36608e14bc1SMauro Carvalho Chehab try: 36708e14bc1SMauro Carvalho Chehab subprocess.run(latex_cmd + [path], 36808e14bc1SMauro Carvalho Chehab cwd=from_dir, check=True, env=self.env) 36908e14bc1SMauro Carvalho Chehab 37008e14bc1SMauro Carvalho Chehab return True 37108e14bc1SMauro Carvalho Chehab except subprocess.CalledProcessError: 37208e14bc1SMauro Carvalho Chehab return False 37308e14bc1SMauro Carvalho Chehab 37408e14bc1SMauro Carvalho Chehab def pdf_parallel_build(self, tex_suffix, latex_cmd, tex_files, n_jobs): 37508e14bc1SMauro Carvalho Chehab """Build PDF files in parallel if possible""" 37608e14bc1SMauro Carvalho Chehab builds = {} 37708e14bc1SMauro Carvalho Chehab build_failed = False 37808e14bc1SMauro Carvalho Chehab max_len = 0 37908e14bc1SMauro Carvalho Chehab has_tex = False 38008e14bc1SMauro Carvalho Chehab 38108e14bc1SMauro Carvalho Chehab # 38208e14bc1SMauro Carvalho Chehab # LaTeX PDF error code is almost useless for us: 38308e14bc1SMauro Carvalho Chehab # any warning makes it non-zero. For kernel doc builds it always return 38408e14bc1SMauro Carvalho Chehab # non-zero even when build succeeds. So, let's do the best next thing: 38508e14bc1SMauro Carvalho Chehab # Ignore build errors. At the end, check if all PDF files were built, 38608e14bc1SMauro Carvalho Chehab # printing a summary with the built ones and returning 0 if all of 38708e14bc1SMauro Carvalho Chehab # them were actually built. 38808e14bc1SMauro Carvalho Chehab # 38908e14bc1SMauro Carvalho Chehab with futures.ThreadPoolExecutor(max_workers=n_jobs) as executor: 39008e14bc1SMauro Carvalho Chehab jobs = {} 39108e14bc1SMauro Carvalho Chehab 39208e14bc1SMauro Carvalho Chehab for from_dir, pdf_dir, entry in tex_files: 39308e14bc1SMauro Carvalho Chehab name = entry.name 39408e14bc1SMauro Carvalho Chehab 39508e14bc1SMauro Carvalho Chehab if not name.endswith(tex_suffix): 39608e14bc1SMauro Carvalho Chehab continue 39708e14bc1SMauro Carvalho Chehab 39808e14bc1SMauro Carvalho Chehab name = name[:-len(tex_suffix)] 39908e14bc1SMauro Carvalho Chehab has_tex = True 40008e14bc1SMauro Carvalho Chehab 40108e14bc1SMauro Carvalho Chehab future = executor.submit(self.build_pdf_file, latex_cmd, 40208e14bc1SMauro Carvalho Chehab from_dir, entry.path) 40308e14bc1SMauro Carvalho Chehab jobs[future] = (from_dir, pdf_dir, name) 40408e14bc1SMauro Carvalho Chehab 40508e14bc1SMauro Carvalho Chehab for future in futures.as_completed(jobs): 40608e14bc1SMauro Carvalho Chehab from_dir, pdf_dir, name = jobs[future] 40708e14bc1SMauro Carvalho Chehab 40808e14bc1SMauro Carvalho Chehab pdf_name = name + ".pdf" 40908e14bc1SMauro Carvalho Chehab pdf_from = os.path.join(from_dir, pdf_name) 4100d9abc76SMauro Carvalho Chehab pdf_to = os.path.join(pdf_dir, pdf_name) 4110d9abc76SMauro Carvalho Chehab out_name = os.path.relpath(pdf_to, self.builddir) 4120d9abc76SMauro Carvalho Chehab max_len = max(max_len, len(out_name)) 41308e14bc1SMauro Carvalho Chehab 41408e14bc1SMauro Carvalho Chehab try: 41508e14bc1SMauro Carvalho Chehab success = future.result() 41608e14bc1SMauro Carvalho Chehab 41708e14bc1SMauro Carvalho Chehab if success and os.path.exists(pdf_from): 41808e14bc1SMauro Carvalho Chehab os.rename(pdf_from, pdf_to) 41908e14bc1SMauro Carvalho Chehab 42008e14bc1SMauro Carvalho Chehab # 42108e14bc1SMauro Carvalho Chehab # if verbose, get the name of built PDF file 42208e14bc1SMauro Carvalho Chehab # 42308e14bc1SMauro Carvalho Chehab if self.verbose: 4240d9abc76SMauro Carvalho Chehab builds[out_name] = "SUCCESS" 42508e14bc1SMauro Carvalho Chehab else: 4260d9abc76SMauro Carvalho Chehab builds[out_name] = "FAILED" 42708e14bc1SMauro Carvalho Chehab build_failed = True 42808e14bc1SMauro Carvalho Chehab except futures.Error as e: 4290d9abc76SMauro Carvalho Chehab builds[out_name] = f"FAILED ({repr(e)})" 43008e14bc1SMauro Carvalho Chehab build_failed = True 43108e14bc1SMauro Carvalho Chehab 43208e14bc1SMauro Carvalho Chehab # 43308e14bc1SMauro Carvalho Chehab # Handle case where no .tex files were found 43408e14bc1SMauro Carvalho Chehab # 43508e14bc1SMauro Carvalho Chehab if not has_tex: 4360d9abc76SMauro Carvalho Chehab out_name = "LaTeX files" 4370d9abc76SMauro Carvalho Chehab max_len = max(max_len, len(out_name)) 4380d9abc76SMauro Carvalho Chehab builds[out_name] = "FAILED: no .tex files were generated" 43908e14bc1SMauro Carvalho Chehab build_failed = True 44008e14bc1SMauro Carvalho Chehab 44108e14bc1SMauro Carvalho Chehab return builds, build_failed, max_len 44208e14bc1SMauro Carvalho Chehab 443819667bcSMauro Carvalho Chehab def handle_pdf(self, output_dirs, deny_vf): 444819667bcSMauro Carvalho Chehab """ 445819667bcSMauro Carvalho Chehab Extra steps for PDF output. 446819667bcSMauro Carvalho Chehab 447819667bcSMauro Carvalho Chehab As PDF is handled via a LaTeX output, after building the .tex file, 448819667bcSMauro Carvalho Chehab a new build is needed to create the PDF output from the latex 449819667bcSMauro Carvalho Chehab directory. 450819667bcSMauro Carvalho Chehab """ 451819667bcSMauro Carvalho Chehab builds = {} 452819667bcSMauro Carvalho Chehab max_len = 0 45308e14bc1SMauro Carvalho Chehab tex_suffix = ".tex" 45408e14bc1SMauro Carvalho Chehab tex_files = [] 455819667bcSMauro Carvalho Chehab 456819667bcSMauro Carvalho Chehab # 457819667bcSMauro Carvalho Chehab # Since early 2024, Fedora and openSUSE tumbleweed have started 458819667bcSMauro Carvalho Chehab # deploying variable-font format of "Noto CJK", causing LaTeX 459819667bcSMauro Carvalho Chehab # to break with CJK. Work around it, by denying the variable font 460819667bcSMauro Carvalho Chehab # usage during xelatex build by passing the location of a config 461819667bcSMauro Carvalho Chehab # file with a deny list. 462819667bcSMauro Carvalho Chehab # 463819667bcSMauro Carvalho Chehab # See tools/docs/lib/latex_fonts.py for more details. 464819667bcSMauro Carvalho Chehab # 465819667bcSMauro Carvalho Chehab if deny_vf: 466819667bcSMauro Carvalho Chehab deny_vf = os.path.expanduser(deny_vf) 467819667bcSMauro Carvalho Chehab if os.path.isdir(deny_vf): 468819667bcSMauro Carvalho Chehab self.env["XDG_CONFIG_HOME"] = deny_vf 469819667bcSMauro Carvalho Chehab 470819667bcSMauro Carvalho Chehab for from_dir in output_dirs: 471819667bcSMauro Carvalho Chehab pdf_dir = os.path.join(from_dir, "../pdf") 472819667bcSMauro Carvalho Chehab os.makedirs(pdf_dir, exist_ok=True) 473819667bcSMauro Carvalho Chehab 474819667bcSMauro Carvalho Chehab if self.latexmk_cmd: 475819667bcSMauro Carvalho Chehab latex_cmd = [self.latexmk_cmd, f"-{self.pdflatex}"] 476819667bcSMauro Carvalho Chehab else: 477819667bcSMauro Carvalho Chehab latex_cmd = [self.pdflatex] 478819667bcSMauro Carvalho Chehab 479819667bcSMauro Carvalho Chehab latex_cmd.extend(shlex.split(self.latexopts)) 480819667bcSMauro Carvalho Chehab 48108e14bc1SMauro Carvalho Chehab # Get a list of tex files to process 482819667bcSMauro Carvalho Chehab with os.scandir(from_dir) as it: 483819667bcSMauro Carvalho Chehab for entry in it: 48408e14bc1SMauro Carvalho Chehab if entry.name.endswith(tex_suffix): 48508e14bc1SMauro Carvalho Chehab tex_files.append((from_dir, pdf_dir, entry)) 486819667bcSMauro Carvalho Chehab 487819667bcSMauro Carvalho Chehab # 48808e14bc1SMauro Carvalho Chehab # When using make, this won't be used, as the number of jobs comes 48908e14bc1SMauro Carvalho Chehab # from POSIX jobserver. So, this covers the case where build comes 49008e14bc1SMauro Carvalho Chehab # from command line. On such case, serialize by default, except if 49108e14bc1SMauro Carvalho Chehab # the user explicitly sets the number of jobs. 492819667bcSMauro Carvalho Chehab # 49308e14bc1SMauro Carvalho Chehab n_jobs = 1 49408e14bc1SMauro Carvalho Chehab 49508e14bc1SMauro Carvalho Chehab # n_jobs is either an integer or "auto". Only use it if it is a number 49608e14bc1SMauro Carvalho Chehab if self.n_jobs: 497819667bcSMauro Carvalho Chehab try: 49808e14bc1SMauro Carvalho Chehab n_jobs = int(self.n_jobs) 49908e14bc1SMauro Carvalho Chehab except ValueError: 500819667bcSMauro Carvalho Chehab pass 501819667bcSMauro Carvalho Chehab 50208e14bc1SMauro Carvalho Chehab # 50308e14bc1SMauro Carvalho Chehab # When using make, jobserver.claim is the number of jobs that were 50408e14bc1SMauro Carvalho Chehab # used with "-j" and that aren't used by other make targets 50508e14bc1SMauro Carvalho Chehab # 50608e14bc1SMauro Carvalho Chehab with JobserverExec() as jobserver: 50708e14bc1SMauro Carvalho Chehab n_jobs = 1 508819667bcSMauro Carvalho Chehab 50908e14bc1SMauro Carvalho Chehab # 51008e14bc1SMauro Carvalho Chehab # Handle the case when a parameter is passed via command line, 51108e14bc1SMauro Carvalho Chehab # using it as default, if jobserver doesn't claim anything 51208e14bc1SMauro Carvalho Chehab # 51308e14bc1SMauro Carvalho Chehab if self.n_jobs: 51408e14bc1SMauro Carvalho Chehab try: 51508e14bc1SMauro Carvalho Chehab n_jobs = int(self.n_jobs) 51608e14bc1SMauro Carvalho Chehab except ValueError: 51708e14bc1SMauro Carvalho Chehab pass 518819667bcSMauro Carvalho Chehab 51908e14bc1SMauro Carvalho Chehab if jobserver.claim: 52008e14bc1SMauro Carvalho Chehab n_jobs = jobserver.claim 521819667bcSMauro Carvalho Chehab 52208e14bc1SMauro Carvalho Chehab builds, build_failed, max_len = self.pdf_parallel_build(tex_suffix, 52308e14bc1SMauro Carvalho Chehab latex_cmd, 52408e14bc1SMauro Carvalho Chehab tex_files, 52508e14bc1SMauro Carvalho Chehab n_jobs) 526819667bcSMauro Carvalho Chehab 52708e14bc1SMauro Carvalho Chehab # 52808e14bc1SMauro Carvalho Chehab # In verbose mode, print a summary with the build results per file. 52908e14bc1SMauro Carvalho Chehab # Otherwise, print a single line with all failures, if any. 53008e14bc1SMauro Carvalho Chehab # On both cases, return code 1 indicates build failures, 53108e14bc1SMauro Carvalho Chehab # 53208e14bc1SMauro Carvalho Chehab if self.verbose: 533819667bcSMauro Carvalho Chehab msg = "Summary" 534819667bcSMauro Carvalho Chehab msg += "\n" + "=" * len(msg) 535819667bcSMauro Carvalho Chehab print() 536819667bcSMauro Carvalho Chehab print(msg) 537819667bcSMauro Carvalho Chehab 538819667bcSMauro Carvalho Chehab for pdf_name, pdf_file in builds.items(): 539819667bcSMauro Carvalho Chehab print(f"{pdf_name:<{max_len}}: {pdf_file}") 540819667bcSMauro Carvalho Chehab 541819667bcSMauro Carvalho Chehab print() 542819667bcSMauro Carvalho Chehab if build_failed: 543819667bcSMauro Carvalho Chehab msg = LatexFontChecker().check() 544819667bcSMauro Carvalho Chehab if msg: 545819667bcSMauro Carvalho Chehab print(msg) 546819667bcSMauro Carvalho Chehab 54708e14bc1SMauro Carvalho Chehab sys.exit("Error: not all PDF files were created.") 54808e14bc1SMauro Carvalho Chehab 54908e14bc1SMauro Carvalho Chehab elif build_failed: 55008e14bc1SMauro Carvalho Chehab n_failures = len(builds) 55108e14bc1SMauro Carvalho Chehab failures = ", ".join(builds.keys()) 55208e14bc1SMauro Carvalho Chehab 55308e14bc1SMauro Carvalho Chehab msg = LatexFontChecker().check() 55408e14bc1SMauro Carvalho Chehab if msg: 55508e14bc1SMauro Carvalho Chehab print(msg) 55608e14bc1SMauro Carvalho Chehab 55708e14bc1SMauro Carvalho Chehab sys.exit(f"Error: Can't build {n_failures} PDF file(s): {failures}") 558819667bcSMauro Carvalho Chehab 559819667bcSMauro Carvalho Chehab def handle_info(self, output_dirs): 560819667bcSMauro Carvalho Chehab """ 561819667bcSMauro Carvalho Chehab Extra steps for Info output. 562819667bcSMauro Carvalho Chehab 563819667bcSMauro Carvalho Chehab For texinfo generation, an additional make is needed from the 564819667bcSMauro Carvalho Chehab texinfo directory. 565819667bcSMauro Carvalho Chehab """ 566819667bcSMauro Carvalho Chehab 567819667bcSMauro Carvalho Chehab for output_dir in output_dirs: 568819667bcSMauro Carvalho Chehab try: 569819667bcSMauro Carvalho Chehab subprocess.run(["make", "info"], cwd=output_dir, check=True) 570819667bcSMauro Carvalho Chehab except subprocess.CalledProcessError as e: 571819667bcSMauro Carvalho Chehab sys.exit(f"Error generating info docs: {e}") 572819667bcSMauro Carvalho Chehab 5737e8a8143SMauro Carvalho Chehab def handle_man(self, kerneldoc, docs_dir, src_dir, output_dir): 5747e8a8143SMauro Carvalho Chehab """ 5757e8a8143SMauro Carvalho Chehab Create man pages from kernel-doc output 5767e8a8143SMauro Carvalho Chehab """ 5777e8a8143SMauro Carvalho Chehab 5787e8a8143SMauro Carvalho Chehab re_kernel_doc = re.compile(r"^\.\.\s+kernel-doc::\s*(\S+)") 5797e8a8143SMauro Carvalho Chehab re_man = re.compile(r'^\.TH "[^"]*" (\d+) "([^"]*)"') 5807e8a8143SMauro Carvalho Chehab 5817e8a8143SMauro Carvalho Chehab if docs_dir == src_dir: 5827e8a8143SMauro Carvalho Chehab # 5837e8a8143SMauro Carvalho Chehab # Pick the entire set of kernel-doc markups from the entire tree 5847e8a8143SMauro Carvalho Chehab # 5857e8a8143SMauro Carvalho Chehab kdoc_files = set([self.srctree]) 5867e8a8143SMauro Carvalho Chehab else: 5877e8a8143SMauro Carvalho Chehab kdoc_files = set() 5887e8a8143SMauro Carvalho Chehab 5897e8a8143SMauro Carvalho Chehab for fname in glob(os.path.join(src_dir, "**"), recursive=True): 5907e8a8143SMauro Carvalho Chehab if os.path.isfile(fname) and fname.endswith(".rst"): 5917e8a8143SMauro Carvalho Chehab with open(fname, "r", encoding="utf-8") as in_fp: 5927e8a8143SMauro Carvalho Chehab data = in_fp.read() 5937e8a8143SMauro Carvalho Chehab 5947e8a8143SMauro Carvalho Chehab for line in data.split("\n"): 5957e8a8143SMauro Carvalho Chehab match = re_kernel_doc.match(line) 5967e8a8143SMauro Carvalho Chehab if match: 5977e8a8143SMauro Carvalho Chehab if os.path.isfile(match.group(1)): 5987e8a8143SMauro Carvalho Chehab kdoc_files.add(match.group(1)) 5997e8a8143SMauro Carvalho Chehab 6007e8a8143SMauro Carvalho Chehab if not kdoc_files: 6017e8a8143SMauro Carvalho Chehab sys.exit(f"Directory {src_dir} doesn't contain kernel-doc tags") 6027e8a8143SMauro Carvalho Chehab 6037e8a8143SMauro Carvalho Chehab cmd = [ kerneldoc, "-m" ] + sorted(kdoc_files) 6047e8a8143SMauro Carvalho Chehab try: 6057e8a8143SMauro Carvalho Chehab if self.verbose: 6067e8a8143SMauro Carvalho Chehab print(" ".join(cmd)) 6077e8a8143SMauro Carvalho Chehab 6087e8a8143SMauro Carvalho Chehab result = subprocess.run(cmd, stdout=subprocess.PIPE, text= True) 6097e8a8143SMauro Carvalho Chehab 6107e8a8143SMauro Carvalho Chehab if result.returncode: 6117e8a8143SMauro Carvalho Chehab print(f"Warning: kernel-doc returned {result.returncode} warnings") 6127e8a8143SMauro Carvalho Chehab 6137e8a8143SMauro Carvalho Chehab except (OSError, ValueError, subprocess.SubprocessError) as e: 6147e8a8143SMauro Carvalho Chehab sys.exit(f"Failed to create man pages for {src_dir}: {repr(e)}") 6157e8a8143SMauro Carvalho Chehab 6167e8a8143SMauro Carvalho Chehab fp = None 6177e8a8143SMauro Carvalho Chehab try: 6187e8a8143SMauro Carvalho Chehab for line in result.stdout.split("\n"): 6197e8a8143SMauro Carvalho Chehab match = re_man.match(line) 6207e8a8143SMauro Carvalho Chehab if not match: 6217e8a8143SMauro Carvalho Chehab if fp: 6227e8a8143SMauro Carvalho Chehab fp.write(line + '\n') 6237e8a8143SMauro Carvalho Chehab continue 6247e8a8143SMauro Carvalho Chehab 6257e8a8143SMauro Carvalho Chehab if fp: 6267e8a8143SMauro Carvalho Chehab fp.close() 6277e8a8143SMauro Carvalho Chehab 6287e8a8143SMauro Carvalho Chehab fname = f"{output_dir}/{match.group(2)}.{match.group(1)}" 6297e8a8143SMauro Carvalho Chehab 6307e8a8143SMauro Carvalho Chehab if self.verbose: 6317e8a8143SMauro Carvalho Chehab print(f"Creating {fname}") 6327e8a8143SMauro Carvalho Chehab fp = open(fname, "w", encoding="utf-8") 6337e8a8143SMauro Carvalho Chehab fp.write(line + '\n') 6347e8a8143SMauro Carvalho Chehab finally: 6357e8a8143SMauro Carvalho Chehab if fp: 6367e8a8143SMauro Carvalho Chehab fp.close() 6377e8a8143SMauro Carvalho Chehab 638819667bcSMauro Carvalho Chehab def cleandocs(self, builder): # pylint: disable=W0613 639819667bcSMauro Carvalho Chehab """Remove documentation output directory""" 640819667bcSMauro Carvalho Chehab shutil.rmtree(self.builddir, ignore_errors=True) 641819667bcSMauro Carvalho Chehab 64272603d73SMauro Carvalho Chehab def build(self, target, sphinxdirs=None, 643464257baSMauro Carvalho Chehab theme=None, css=None, paper=None, deny_vf=None, 6444c6ece91SMauro Carvalho Chehab skip_sphinx=False): 645819667bcSMauro Carvalho Chehab """ 646819667bcSMauro Carvalho Chehab Build documentation using Sphinx. This is the core function of this 647819667bcSMauro Carvalho Chehab module. It prepares all arguments required by sphinx-build. 648819667bcSMauro Carvalho Chehab """ 649819667bcSMauro Carvalho Chehab 650819667bcSMauro Carvalho Chehab builder = TARGETS[target]["builder"] 651819667bcSMauro Carvalho Chehab out_dir = TARGETS[target].get("out_dir", "") 652819667bcSMauro Carvalho Chehab 653819667bcSMauro Carvalho Chehab # 654819667bcSMauro Carvalho Chehab # Cleandocs doesn't require sphinx-build 655819667bcSMauro Carvalho Chehab # 656819667bcSMauro Carvalho Chehab if target == "cleandocs": 657819667bcSMauro Carvalho Chehab self.cleandocs(builder) 658819667bcSMauro Carvalho Chehab return 659819667bcSMauro Carvalho Chehab 660819667bcSMauro Carvalho Chehab if theme: 661819667bcSMauro Carvalho Chehab os.environ["DOCS_THEME"] = theme 662819667bcSMauro Carvalho Chehab 663819667bcSMauro Carvalho Chehab # 664819667bcSMauro Carvalho Chehab # Other targets require sphinx-build, so check if it exists 665819667bcSMauro Carvalho Chehab # 6664c6ece91SMauro Carvalho Chehab if not skip_sphinx: 667819667bcSMauro Carvalho Chehab sphinxbuild = shutil.which(self.sphinxbuild, path=self.env["PATH"]) 6687e8a8143SMauro Carvalho Chehab if not sphinxbuild and target != "mandocs": 669819667bcSMauro Carvalho Chehab sys.exit(f"Error: {self.sphinxbuild} not found in PATH.\n") 670819667bcSMauro Carvalho Chehab 6715401f971SMauro Carvalho Chehab if target == "pdfdocs": 672819667bcSMauro Carvalho Chehab if not self.pdflatex_cmd and not self.latexmk_cmd: 673819667bcSMauro Carvalho Chehab sys.exit("Error: pdflatex or latexmk required for PDF generation") 674819667bcSMauro Carvalho Chehab 675819667bcSMauro Carvalho Chehab docs_dir = os.path.abspath(os.path.join(self.srctree, "Documentation")) 676819667bcSMauro Carvalho Chehab 677819667bcSMauro Carvalho Chehab # 678819667bcSMauro Carvalho Chehab # Fill in base arguments for Sphinx build 679819667bcSMauro Carvalho Chehab # 680819667bcSMauro Carvalho Chehab kerneldoc = self.kerneldoc 681819667bcSMauro Carvalho Chehab if kerneldoc.startswith(self.srctree): 682819667bcSMauro Carvalho Chehab kerneldoc = os.path.relpath(kerneldoc, self.srctree) 683819667bcSMauro Carvalho Chehab 6846f9a96ccSThomas Weißschuh if not sphinxdirs: 6856f9a96ccSThomas Weißschuh sphinxdirs = os.environ.get("SPHINXDIRS", ".") 6866f9a96ccSThomas Weißschuh 6876f9a96ccSThomas Weißschuh # 6886f9a96ccSThomas Weißschuh # sphinxdirs can be a list or a whitespace-separated string 6896f9a96ccSThomas Weißschuh # 6906f9a96ccSThomas Weißschuh sphinxdirs_list = [] 6916f9a96ccSThomas Weißschuh for sphinxdir in sphinxdirs: 6926f9a96ccSThomas Weißschuh if isinstance(sphinxdir, list): 6936f9a96ccSThomas Weißschuh sphinxdirs_list += sphinxdir 6946f9a96ccSThomas Weißschuh else: 6956f9a96ccSThomas Weißschuh sphinxdirs_list += sphinxdir.split() 6966f9a96ccSThomas Weißschuh 697819667bcSMauro Carvalho Chehab args = [ "-b", builder, "-c", docs_dir ] 698819667bcSMauro Carvalho Chehab 699819667bcSMauro Carvalho Chehab if builder == "latex": 700819667bcSMauro Carvalho Chehab if not paper: 701819667bcSMauro Carvalho Chehab paper = PAPER[1] 702819667bcSMauro Carvalho Chehab 703819667bcSMauro Carvalho Chehab args.extend(["-D", f"latex_elements.papersize={paper}paper"]) 704819667bcSMauro Carvalho Chehab 705ffb569d5SThomas Weißschuh rustdoc = self.check_rust(sphinxdirs_list) 7062d652135SThomas Weißschuh if rustdoc: 707819667bcSMauro Carvalho Chehab args.extend(["-t", "rustdoc"]) 708819667bcSMauro Carvalho Chehab 709819667bcSMauro Carvalho Chehab # 71082c294d4SMauro Carvalho Chehab # The sphinx-build tool has a bug: internally, it tries to set 71182c294d4SMauro Carvalho Chehab # locale with locale.setlocale(locale.LC_ALL, ''). This causes a 71282c294d4SMauro Carvalho Chehab # crash if language is not set. Detect and fix it. 71382c294d4SMauro Carvalho Chehab # 71482c294d4SMauro Carvalho Chehab try: 71582c294d4SMauro Carvalho Chehab locale.setlocale(locale.LC_ALL, '') 71682c294d4SMauro Carvalho Chehab except locale.Error: 71782c294d4SMauro Carvalho Chehab self.env["LC_ALL"] = "C" 71882c294d4SMauro Carvalho Chehab 71982c294d4SMauro Carvalho Chehab # 720819667bcSMauro Carvalho Chehab # Step 1: Build each directory in separate. 721819667bcSMauro Carvalho Chehab # 722819667bcSMauro Carvalho Chehab # This is not the best way of handling it, as cross-references between 723819667bcSMauro Carvalho Chehab # them will be broken, but this is what we've been doing since 724819667bcSMauro Carvalho Chehab # the beginning. 725819667bcSMauro Carvalho Chehab # 726819667bcSMauro Carvalho Chehab output_dirs = [] 727819667bcSMauro Carvalho Chehab for sphinxdir in sphinxdirs_list: 728819667bcSMauro Carvalho Chehab src_dir = os.path.join(docs_dir, sphinxdir) 729819667bcSMauro Carvalho Chehab doctree_dir = os.path.join(self.builddir, ".doctrees") 730819667bcSMauro Carvalho Chehab output_dir = os.path.join(self.builddir, sphinxdir, out_dir) 731819667bcSMauro Carvalho Chehab 732819667bcSMauro Carvalho Chehab # 733819667bcSMauro Carvalho Chehab # Make directory names canonical 734819667bcSMauro Carvalho Chehab # 735819667bcSMauro Carvalho Chehab src_dir = os.path.normpath(src_dir) 736819667bcSMauro Carvalho Chehab doctree_dir = os.path.normpath(doctree_dir) 737819667bcSMauro Carvalho Chehab output_dir = os.path.normpath(output_dir) 738819667bcSMauro Carvalho Chehab 739819667bcSMauro Carvalho Chehab os.makedirs(doctree_dir, exist_ok=True) 740819667bcSMauro Carvalho Chehab os.makedirs(output_dir, exist_ok=True) 741819667bcSMauro Carvalho Chehab 742819667bcSMauro Carvalho Chehab output_dirs.append(output_dir) 743819667bcSMauro Carvalho Chehab 744819667bcSMauro Carvalho Chehab build_args = args + [ 745819667bcSMauro Carvalho Chehab "-d", doctree_dir, 746819667bcSMauro Carvalho Chehab "-D", f"version={self.kernelversion}", 747819667bcSMauro Carvalho Chehab "-D", f"release={self.kernelrelease}", 748819667bcSMauro Carvalho Chehab "-D", f"kerneldoc_srctree={self.srctree}", 749819667bcSMauro Carvalho Chehab src_dir, 750819667bcSMauro Carvalho Chehab output_dir, 751819667bcSMauro Carvalho Chehab ] 752819667bcSMauro Carvalho Chehab 7537e8a8143SMauro Carvalho Chehab if target == "mandocs": 7547e8a8143SMauro Carvalho Chehab self.handle_man(kerneldoc, docs_dir, src_dir, output_dir) 7554c6ece91SMauro Carvalho Chehab elif not skip_sphinx: 756819667bcSMauro Carvalho Chehab try: 7570aa9c039SMauro Carvalho Chehab result = self.run_sphinx(sphinxbuild, build_args, 7580aa9c039SMauro Carvalho Chehab env=self.env) 7590aa9c039SMauro Carvalho Chehab 7600aa9c039SMauro Carvalho Chehab if result: 7610aa9c039SMauro Carvalho Chehab sys.exit(f"Build failed: return code: {result}") 7620aa9c039SMauro Carvalho Chehab 763819667bcSMauro Carvalho Chehab except (OSError, ValueError, subprocess.SubprocessError) as e: 764819667bcSMauro Carvalho Chehab sys.exit(f"Build failed: {repr(e)}") 765819667bcSMauro Carvalho Chehab 766819667bcSMauro Carvalho Chehab # 767819667bcSMauro Carvalho Chehab # Ensure that each html/epub output will have needed static files 768819667bcSMauro Carvalho Chehab # 769819667bcSMauro Carvalho Chehab if target in ["htmldocs", "epubdocs"]: 770464257baSMauro Carvalho Chehab self.handle_html(css, output_dir) 771819667bcSMauro Carvalho Chehab 772819667bcSMauro Carvalho Chehab # 773819667bcSMauro Carvalho Chehab # Step 2: Some targets (PDF and info) require an extra step once 774819667bcSMauro Carvalho Chehab # sphinx-build finishes 775819667bcSMauro Carvalho Chehab # 776819667bcSMauro Carvalho Chehab if target == "pdfdocs": 777819667bcSMauro Carvalho Chehab self.handle_pdf(output_dirs, deny_vf) 778819667bcSMauro Carvalho Chehab elif target == "infodocs": 779819667bcSMauro Carvalho Chehab self.handle_info(output_dirs) 780819667bcSMauro Carvalho Chehab 7812d652135SThomas Weißschuh if rustdoc and target in ["htmldocs", "epubdocs"]: 7825094f7d5SThomas Weißschuh print("Building rust docs") 7835094f7d5SThomas Weißschuh if "MAKE" in self.env: 7845094f7d5SThomas Weißschuh cmd = [self.env["MAKE"]] 7855094f7d5SThomas Weißschuh else: 7865094f7d5SThomas Weißschuh cmd = ["make", "LLVM=1"] 7875094f7d5SThomas Weißschuh 7885094f7d5SThomas Weißschuh cmd += [ "rustdoc"] 7895094f7d5SThomas Weißschuh if self.verbose: 7905094f7d5SThomas Weißschuh print(" ".join(cmd)) 7915094f7d5SThomas Weißschuh 7925094f7d5SThomas Weißschuh try: 7935094f7d5SThomas Weißschuh subprocess.run(cmd, check=True) 7945094f7d5SThomas Weißschuh except subprocess.CalledProcessError as e: 7955094f7d5SThomas Weißschuh print(f"Ignored errors when building rustdoc: {e}. Is RUST enabled?", 7965094f7d5SThomas Weißschuh file=sys.stderr) 7975094f7d5SThomas Weißschuh 798819667bcSMauro Carvalho Chehabdef jobs_type(value): 799819667bcSMauro Carvalho Chehab """ 800819667bcSMauro Carvalho Chehab Handle valid values for -j. Accepts Sphinx "-jauto", plus a number 801819667bcSMauro Carvalho Chehab equal or bigger than one. 802819667bcSMauro Carvalho Chehab """ 803819667bcSMauro Carvalho Chehab if value is None: 804819667bcSMauro Carvalho Chehab return None 805819667bcSMauro Carvalho Chehab 806819667bcSMauro Carvalho Chehab if value.lower() == 'auto': 807819667bcSMauro Carvalho Chehab return value.lower() 808819667bcSMauro Carvalho Chehab 809819667bcSMauro Carvalho Chehab try: 810819667bcSMauro Carvalho Chehab if int(value) >= 1: 811819667bcSMauro Carvalho Chehab return value 812819667bcSMauro Carvalho Chehab 813819667bcSMauro Carvalho Chehab raise argparse.ArgumentTypeError(f"Minimum jobs is 1, got {value}") 814819667bcSMauro Carvalho Chehab except ValueError: 815819667bcSMauro Carvalho Chehab raise argparse.ArgumentTypeError(f"Must be 'auto' or positive integer, got {value}") # pylint: disable=W0707 816819667bcSMauro Carvalho Chehab 817*64e4882cSMauro Carvalho ChehabEPILOG=""" 818*64e4882cSMauro Carvalho ChehabBesides the command line arguments, several environment variables affect its 819*64e4882cSMauro Carvalho Chehabdefault behavior, meant to be used when called via Kernel Makefile: 820*64e4882cSMauro Carvalho Chehab 821*64e4882cSMauro Carvalho Chehab- KERNELVERSION: Kernel major version 822*64e4882cSMauro Carvalho Chehab- KERNELRELEASE: Kernel release 823*64e4882cSMauro Carvalho Chehab- KBUILD_VERBOSE: Contains the value of "make V=[0|1] variable. 824*64e4882cSMauro Carvalho Chehab When V=0 (KBUILD_VERBOSE=0), sets verbose level to "-q". 825*64e4882cSMauro Carvalho Chehab- SPHINXBUILD: Documentation build tool (default: "sphinx-build"). 826*64e4882cSMauro Carvalho Chehab- SPHINXOPTS: Extra options pased to SPHINXBUILD 827*64e4882cSMauro Carvalho Chehab (default: "-j auto" and "-q" if KBUILD_VERBOSE=0). 828*64e4882cSMauro Carvalho Chehab The "-v" flag can be used to increase verbosity. 829*64e4882cSMauro Carvalho Chehab If V=0, the first "-v" will drop "-q". 830*64e4882cSMauro Carvalho Chehab- PYTHON3: Python command to run SPHINXBUILD 831*64e4882cSMauro Carvalho Chehab- PDFLATEX: LaTeX PDF engine. (default: "xelatex") 832*64e4882cSMauro Carvalho Chehab- LATEXOPTS: Optional set of command line arguments to the LaTeX engine 833*64e4882cSMauro Carvalho Chehab- srctree: Location of the Kernel root directory (default: "."). 834*64e4882cSMauro Carvalho Chehab 835*64e4882cSMauro Carvalho Chehab""" 836*64e4882cSMauro Carvalho Chehab 837819667bcSMauro Carvalho Chehabdef main(): 838819667bcSMauro Carvalho Chehab """ 839819667bcSMauro Carvalho Chehab Main function. The only mandatory argument is the target. If not 840819667bcSMauro Carvalho Chehab specified, the other arguments will use default values if not 841819667bcSMauro Carvalho Chehab specified at os.environ. 842819667bcSMauro Carvalho Chehab """ 843*64e4882cSMauro Carvalho Chehab parser = argparse.ArgumentParser(formatter_class=argparse.RawTextHelpFormatter, 844*64e4882cSMauro Carvalho Chehab description=__doc__, 845*64e4882cSMauro Carvalho Chehab epilog=EPILOG) 846819667bcSMauro Carvalho Chehab 847819667bcSMauro Carvalho Chehab parser.add_argument("target", choices=list(TARGETS.keys()), 848819667bcSMauro Carvalho Chehab help="Documentation target to build") 849819667bcSMauro Carvalho Chehab parser.add_argument("--sphinxdirs", nargs="+", 850819667bcSMauro Carvalho Chehab help="Specific directories to build") 851819667bcSMauro Carvalho Chehab parser.add_argument("--builddir", default="output", 852*64e4882cSMauro Carvalho Chehab help="Sphinx configuration file (default: %(default)s)") 853819667bcSMauro Carvalho Chehab 854819667bcSMauro Carvalho Chehab parser.add_argument("--theme", help="Sphinx theme to use") 855819667bcSMauro Carvalho Chehab 856819667bcSMauro Carvalho Chehab parser.add_argument("--css", help="Custom CSS file for HTML/EPUB") 857819667bcSMauro Carvalho Chehab 858819667bcSMauro Carvalho Chehab parser.add_argument("--paper", choices=PAPER, default=PAPER[0], 859819667bcSMauro Carvalho Chehab help="Paper size for LaTeX/PDF output") 860819667bcSMauro Carvalho Chehab 861819667bcSMauro Carvalho Chehab parser.add_argument('--deny-vf', 862819667bcSMauro Carvalho Chehab help="Configuration to deny variable fonts on pdf builds") 863819667bcSMauro Carvalho Chehab 864819667bcSMauro Carvalho Chehab parser.add_argument("-v", "--verbose", action='store_true', 865819667bcSMauro Carvalho Chehab help="place build in verbose mode") 866819667bcSMauro Carvalho Chehab 867819667bcSMauro Carvalho Chehab parser.add_argument('-j', '--jobs', type=jobs_type, 868*64e4882cSMauro Carvalho Chehab help="Sets number of jobs to use with sphinx-build(default: auto)") 869819667bcSMauro Carvalho Chehab 8702f99b85eSMauro Carvalho Chehab parser.add_argument('-i', '--interactive', action='store_true', 8712f99b85eSMauro Carvalho Chehab help="Change latex default to run in interactive mode") 8722f99b85eSMauro Carvalho Chehab 8734c6ece91SMauro Carvalho Chehab parser.add_argument('-s', '--skip-sphinx-build', action='store_true', 8744c6ece91SMauro Carvalho Chehab help="Skip sphinx-build step") 8754c6ece91SMauro Carvalho Chehab 87642180adaSMauro Carvalho Chehab parser.add_argument("-V", "--venv", nargs='?', const=f'{VENV_DEFAULT}', 87742180adaSMauro Carvalho Chehab default=None, 87842180adaSMauro Carvalho Chehab help=f'If used, run Sphinx from a venv dir (default dir: {VENV_DEFAULT})') 87942180adaSMauro Carvalho Chehab 880819667bcSMauro Carvalho Chehab args = parser.parse_args() 881819667bcSMauro Carvalho Chehab 88262ea383bSMauro Carvalho Chehab PythonVersion.check_python(MIN_PYTHON_VERSION, show_alternatives=True, 88362ea383bSMauro Carvalho Chehab bail_out=True) 884819667bcSMauro Carvalho Chehab 88542180adaSMauro Carvalho Chehab builder = SphinxBuilder(builddir=args.builddir, venv=args.venv, 8862f99b85eSMauro Carvalho Chehab verbose=args.verbose, n_jobs=args.jobs, 8872f99b85eSMauro Carvalho Chehab interactive=args.interactive) 888819667bcSMauro Carvalho Chehab 88972603d73SMauro Carvalho Chehab builder.build(args.target, sphinxdirs=args.sphinxdirs, 890819667bcSMauro Carvalho Chehab theme=args.theme, css=args.css, paper=args.paper, 891464257baSMauro Carvalho Chehab deny_vf=args.deny_vf, 8924c6ece91SMauro Carvalho Chehab skip_sphinx=args.skip_sphinx_build) 893819667bcSMauro Carvalho Chehab 894819667bcSMauro Carvalho Chehabif __name__ == "__main__": 895819667bcSMauro Carvalho Chehab main() 896