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 Chehabfrom lib.python_version import PythonVersion 60819667bcSMauro Carvalho Chehabfrom lib.latex_fonts import LatexFontChecker 61819667bcSMauro Carvalho Chehab 62819667bcSMauro Carvalho ChehabLIB_DIR = "../../scripts/lib" 63819667bcSMauro Carvalho ChehabSRC_DIR = os.path.dirname(os.path.realpath(__file__)) 64819667bcSMauro Carvalho Chehab 65819667bcSMauro Carvalho Chehabsys.path.insert(0, os.path.join(SRC_DIR, LIB_DIR)) 66819667bcSMauro Carvalho Chehab 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# 72*42180adaSMauro 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 122819667bcSMauro Carvalho Chehab def get_sphinx_extra_opts(self, n_jobs): 123819667bcSMauro Carvalho Chehab """ 124819667bcSMauro Carvalho Chehab Get the number of jobs to be used for docs build passed via command 125819667bcSMauro Carvalho Chehab line and desired sphinx verbosity. 126819667bcSMauro Carvalho Chehab 127819667bcSMauro Carvalho Chehab The number of jobs can be on different places: 128819667bcSMauro Carvalho Chehab 129819667bcSMauro Carvalho Chehab 1) It can be passed via "-j" argument; 130819667bcSMauro Carvalho Chehab 2) The SPHINXOPTS="-j8" env var may have "-j"; 131819667bcSMauro Carvalho Chehab 3) if called via GNU make, -j specifies the desired number of jobs. 132819667bcSMauro Carvalho Chehab with GNU makefile, this number is available via POSIX jobserver; 133819667bcSMauro Carvalho Chehab 4) if none of the above is available, it should default to "-jauto", 134819667bcSMauro Carvalho Chehab and let sphinx decide the best value. 135819667bcSMauro Carvalho Chehab """ 136819667bcSMauro Carvalho Chehab 137819667bcSMauro Carvalho Chehab # 138819667bcSMauro Carvalho Chehab # SPHINXOPTS env var, if used, contains extra arguments to be used 139819667bcSMauro Carvalho Chehab # by sphinx-build time. Among them, it may contain sphinx verbosity 140819667bcSMauro Carvalho Chehab # and desired number of parallel jobs. 141819667bcSMauro Carvalho Chehab # 142819667bcSMauro Carvalho Chehab parser = argparse.ArgumentParser() 143819667bcSMauro Carvalho Chehab parser.add_argument('-j', '--jobs', type=int) 144819667bcSMauro Carvalho Chehab parser.add_argument('-q', '--quiet', type=int) 145819667bcSMauro Carvalho Chehab 146819667bcSMauro Carvalho Chehab # 147819667bcSMauro Carvalho Chehab # Other sphinx-build arguments go as-is, so place them 148819667bcSMauro Carvalho Chehab # at self.sphinxopts, using shell parser 149819667bcSMauro Carvalho Chehab # 150819667bcSMauro Carvalho Chehab sphinxopts = shlex.split(os.environ.get("SPHINXOPTS", "")) 151819667bcSMauro Carvalho Chehab 152819667bcSMauro Carvalho Chehab # 153819667bcSMauro Carvalho Chehab # Build a list of sphinx args, honoring verbosity here if specified 154819667bcSMauro Carvalho Chehab # 155819667bcSMauro Carvalho Chehab 156819667bcSMauro Carvalho Chehab verbose = self.verbose 157819667bcSMauro Carvalho Chehab sphinx_args, self.sphinxopts = parser.parse_known_args(sphinxopts) 158819667bcSMauro Carvalho Chehab if sphinx_args.quiet is True: 159819667bcSMauro Carvalho Chehab verbose = False 160819667bcSMauro Carvalho Chehab 161819667bcSMauro Carvalho Chehab # 162819667bcSMauro Carvalho Chehab # If the user explicitly sets "-j" at command line, use it. 163819667bcSMauro Carvalho Chehab # Otherwise, pick it from SPHINXOPTS args 164819667bcSMauro Carvalho Chehab # 165819667bcSMauro Carvalho Chehab if n_jobs: 166819667bcSMauro Carvalho Chehab self.n_jobs = n_jobs 167819667bcSMauro Carvalho Chehab elif sphinx_args.jobs: 168819667bcSMauro Carvalho Chehab self.n_jobs = sphinx_args.jobs 169819667bcSMauro Carvalho Chehab else: 170819667bcSMauro Carvalho Chehab self.n_jobs = None 171819667bcSMauro Carvalho Chehab 172819667bcSMauro Carvalho Chehab if not verbose: 173819667bcSMauro Carvalho Chehab self.sphinxopts += ["-q"] 174819667bcSMauro Carvalho Chehab 175*42180adaSMauro Carvalho Chehab def __init__(self, builddir, venv=None, verbose=False, n_jobs=None, 176*42180adaSMauro Carvalho Chehab interactive=None): 177819667bcSMauro Carvalho Chehab """Initialize internal variables""" 178*42180adaSMauro Carvalho Chehab self.venv = venv 179819667bcSMauro Carvalho Chehab self.verbose = None 180819667bcSMauro Carvalho Chehab 181819667bcSMauro Carvalho Chehab # 182819667bcSMauro Carvalho Chehab # Normal variables passed from Kernel's makefile 183819667bcSMauro Carvalho Chehab # 184819667bcSMauro Carvalho Chehab self.kernelversion = os.environ.get("KERNELVERSION", "unknown") 185819667bcSMauro Carvalho Chehab self.kernelrelease = os.environ.get("KERNELRELEASE", "unknown") 186819667bcSMauro Carvalho Chehab self.pdflatex = os.environ.get("PDFLATEX", "xelatex") 1872f99b85eSMauro Carvalho Chehab 1882f99b85eSMauro Carvalho Chehab if not interactive: 189819667bcSMauro Carvalho Chehab self.latexopts = os.environ.get("LATEXOPTS", "-interaction=batchmode -no-shell-escape") 1902f99b85eSMauro Carvalho Chehab else: 1912f99b85eSMauro Carvalho Chehab self.latexopts = os.environ.get("LATEXOPTS", "") 192819667bcSMauro Carvalho Chehab 193819667bcSMauro Carvalho Chehab if not verbose: 194819667bcSMauro Carvalho Chehab verbose = bool(os.environ.get("KBUILD_VERBOSE", "") != "") 195819667bcSMauro Carvalho Chehab 196819667bcSMauro Carvalho Chehab if verbose is not None: 197819667bcSMauro Carvalho Chehab self.verbose = verbose 198819667bcSMauro Carvalho Chehab 199819667bcSMauro Carvalho Chehab # 200819667bcSMauro Carvalho Chehab # Source tree directory. This needs to be at os.environ, as 201819667bcSMauro Carvalho Chehab # Sphinx extensions use it 202819667bcSMauro Carvalho Chehab # 203819667bcSMauro Carvalho Chehab self.srctree = os.environ.get("srctree") 204819667bcSMauro Carvalho Chehab if not self.srctree: 205819667bcSMauro Carvalho Chehab self.srctree = "." 206819667bcSMauro Carvalho Chehab os.environ["srctree"] = self.srctree 207819667bcSMauro Carvalho Chehab 208819667bcSMauro Carvalho Chehab # 209819667bcSMauro Carvalho Chehab # Now that we can expand srctree, get other directories as well 210819667bcSMauro Carvalho Chehab # 211819667bcSMauro Carvalho Chehab self.sphinxbuild = os.environ.get("SPHINXBUILD", "sphinx-build") 212819667bcSMauro Carvalho Chehab self.kerneldoc = self.get_path(os.environ.get("KERNELDOC", 213819667bcSMauro Carvalho Chehab "scripts/kernel-doc.py")) 214819667bcSMauro Carvalho Chehab self.builddir = self.get_path(builddir, use_cwd=True, abs_path=True) 215819667bcSMauro Carvalho Chehab 216819667bcSMauro Carvalho Chehab # 217819667bcSMauro Carvalho Chehab # Get directory locations for LaTeX build toolchain 218819667bcSMauro Carvalho Chehab # 219819667bcSMauro Carvalho Chehab self.pdflatex_cmd = shutil.which(self.pdflatex) 220819667bcSMauro Carvalho Chehab self.latexmk_cmd = shutil.which("latexmk") 221819667bcSMauro Carvalho Chehab 222819667bcSMauro Carvalho Chehab self.env = os.environ.copy() 223819667bcSMauro Carvalho Chehab 224819667bcSMauro Carvalho Chehab self.get_sphinx_extra_opts(n_jobs) 225819667bcSMauro Carvalho Chehab 226*42180adaSMauro Carvalho Chehab # 227*42180adaSMauro Carvalho Chehab # If venv command line argument is specified, run Sphinx from venv 228*42180adaSMauro Carvalho Chehab # 229*42180adaSMauro Carvalho Chehab if venv: 230*42180adaSMauro Carvalho Chehab bin_dir = os.path.join(venv, "bin") 231*42180adaSMauro Carvalho Chehab if not os.path.isfile(os.path.join(bin_dir, "activate")): 232*42180adaSMauro Carvalho Chehab sys.exit(f"Venv {venv} not found.") 233*42180adaSMauro Carvalho Chehab 234*42180adaSMauro Carvalho Chehab # "activate" virtual env 235*42180adaSMauro Carvalho Chehab self.env["PATH"] = bin_dir + ":" + self.env["PATH"] 236*42180adaSMauro Carvalho Chehab self.env["VIRTUAL_ENV"] = venv 237*42180adaSMauro Carvalho Chehab if "PYTHONHOME" in self.env: 238*42180adaSMauro Carvalho Chehab del self.env["PYTHONHOME"] 239*42180adaSMauro Carvalho Chehab print(f"Setting venv to {venv}") 240*42180adaSMauro Carvalho Chehab 241819667bcSMauro Carvalho Chehab def run_sphinx(self, sphinx_build, build_args, *args, **pwargs): 242819667bcSMauro Carvalho Chehab """ 243819667bcSMauro Carvalho Chehab Executes sphinx-build using current python3 command. 244819667bcSMauro Carvalho Chehab 245819667bcSMauro Carvalho Chehab When calling via GNU make, POSIX jobserver is used to tell how 246819667bcSMauro Carvalho Chehab many jobs are still available from a job pool. claim all remaining 247819667bcSMauro Carvalho Chehab jobs, as we don't want sphinx-build to run in parallel with other 248819667bcSMauro Carvalho Chehab jobs. 249819667bcSMauro Carvalho Chehab 250819667bcSMauro Carvalho Chehab Despite that, the user may actually force a different value than 251819667bcSMauro Carvalho Chehab the number of available jobs via command line. 252819667bcSMauro Carvalho Chehab 253819667bcSMauro Carvalho Chehab The "with" logic here is used to ensure that the claimed jobs will 254819667bcSMauro Carvalho Chehab be freed once subprocess finishes 255819667bcSMauro Carvalho Chehab """ 256819667bcSMauro Carvalho Chehab 257819667bcSMauro Carvalho Chehab with JobserverExec() as jobserver: 258819667bcSMauro Carvalho Chehab if jobserver.claim: 259819667bcSMauro Carvalho Chehab # 260819667bcSMauro Carvalho Chehab # when GNU make is used, claim available jobs from jobserver 261819667bcSMauro Carvalho Chehab # 262819667bcSMauro Carvalho Chehab n_jobs = str(jobserver.claim) 263819667bcSMauro Carvalho Chehab else: 264819667bcSMauro Carvalho Chehab # 265819667bcSMauro Carvalho Chehab # Otherwise, let sphinx decide by default 266819667bcSMauro Carvalho Chehab # 267819667bcSMauro Carvalho Chehab n_jobs = "auto" 268819667bcSMauro Carvalho Chehab 269819667bcSMauro Carvalho Chehab # 270819667bcSMauro Carvalho Chehab # If explicitly requested via command line, override default 271819667bcSMauro Carvalho Chehab # 272819667bcSMauro Carvalho Chehab if self.n_jobs: 273819667bcSMauro Carvalho Chehab n_jobs = str(self.n_jobs) 274819667bcSMauro Carvalho Chehab 275*42180adaSMauro Carvalho Chehab if self.venv: 276*42180adaSMauro Carvalho Chehab cmd = ["python"] 277*42180adaSMauro Carvalho Chehab else: 278*42180adaSMauro Carvalho Chehab cmd = [sys.executable,] 279*42180adaSMauro Carvalho Chehab 280*42180adaSMauro Carvalho Chehab cmd += [sphinx_build] 281819667bcSMauro Carvalho Chehab cmd += [f"-j{n_jobs}"] 282819667bcSMauro Carvalho Chehab cmd += self.sphinxopts 283819667bcSMauro Carvalho Chehab cmd += build_args 284819667bcSMauro Carvalho Chehab 285819667bcSMauro Carvalho Chehab if self.verbose: 286819667bcSMauro Carvalho Chehab print(" ".join(cmd)) 287819667bcSMauro Carvalho Chehab 288819667bcSMauro Carvalho Chehab return subprocess.call(cmd, *args, **pwargs) 289819667bcSMauro Carvalho Chehab 2902118ba7dSMauro Carvalho Chehab def handle_html(self, css, output_dir, rustdoc): 291819667bcSMauro Carvalho Chehab """ 292819667bcSMauro Carvalho Chehab Extra steps for HTML and epub output. 293819667bcSMauro Carvalho Chehab 294819667bcSMauro Carvalho Chehab For such targets, we need to ensure that CSS will be properly 295819667bcSMauro Carvalho Chehab copied to the output _static directory 296819667bcSMauro Carvalho Chehab """ 297819667bcSMauro Carvalho Chehab 2982118ba7dSMauro Carvalho Chehab if css: 299819667bcSMauro Carvalho Chehab css = os.path.expanduser(css) 300819667bcSMauro Carvalho Chehab if not css.startswith("/"): 301819667bcSMauro Carvalho Chehab css = os.path.join(self.srctree, css) 302819667bcSMauro Carvalho Chehab 303819667bcSMauro Carvalho Chehab static_dir = os.path.join(output_dir, "_static") 304819667bcSMauro Carvalho Chehab os.makedirs(static_dir, exist_ok=True) 305819667bcSMauro Carvalho Chehab 306819667bcSMauro Carvalho Chehab try: 307819667bcSMauro Carvalho Chehab shutil.copy2(css, static_dir) 308819667bcSMauro Carvalho Chehab except (OSError, IOError) as e: 309819667bcSMauro Carvalho Chehab print(f"Warning: Failed to copy CSS: {e}", file=sys.stderr) 310819667bcSMauro Carvalho Chehab 3112118ba7dSMauro Carvalho Chehab if rustdoc: 3122118ba7dSMauro Carvalho Chehab if "MAKE" in self.env: 3132118ba7dSMauro Carvalho Chehab cmd = [self.env["MAKE"]] 3142118ba7dSMauro Carvalho Chehab else: 3152118ba7dSMauro Carvalho Chehab cmd = ["make", "LLVM=1"] 3162118ba7dSMauro Carvalho Chehab 3172118ba7dSMauro Carvalho Chehab cmd += [ "rustdoc"] 3182118ba7dSMauro Carvalho Chehab if self.verbose: 3192118ba7dSMauro Carvalho Chehab print(" ".join(cmd)) 3202118ba7dSMauro Carvalho Chehab 3212118ba7dSMauro Carvalho Chehab try: 3222118ba7dSMauro Carvalho Chehab subprocess.run(cmd, check=True) 3232118ba7dSMauro Carvalho Chehab except subprocess.CalledProcessError as e: 3242118ba7dSMauro Carvalho Chehab print(f"Ignored errors when building rustdoc: {e}. Is RUST enabled?", 3252118ba7dSMauro Carvalho Chehab file=sys.stderr) 3262118ba7dSMauro Carvalho Chehab 32708e14bc1SMauro Carvalho Chehab def build_pdf_file(self, latex_cmd, from_dir, path): 32808e14bc1SMauro Carvalho Chehab """Builds a single pdf file using latex_cmd""" 32908e14bc1SMauro Carvalho Chehab try: 33008e14bc1SMauro Carvalho Chehab subprocess.run(latex_cmd + [path], 33108e14bc1SMauro Carvalho Chehab cwd=from_dir, check=True, env=self.env) 33208e14bc1SMauro Carvalho Chehab 33308e14bc1SMauro Carvalho Chehab return True 33408e14bc1SMauro Carvalho Chehab except subprocess.CalledProcessError: 33508e14bc1SMauro Carvalho Chehab return False 33608e14bc1SMauro Carvalho Chehab 33708e14bc1SMauro Carvalho Chehab def pdf_parallel_build(self, tex_suffix, latex_cmd, tex_files, n_jobs): 33808e14bc1SMauro Carvalho Chehab """Build PDF files in parallel if possible""" 33908e14bc1SMauro Carvalho Chehab builds = {} 34008e14bc1SMauro Carvalho Chehab build_failed = False 34108e14bc1SMauro Carvalho Chehab max_len = 0 34208e14bc1SMauro Carvalho Chehab has_tex = False 34308e14bc1SMauro Carvalho Chehab 34408e14bc1SMauro Carvalho Chehab # 34508e14bc1SMauro Carvalho Chehab # LaTeX PDF error code is almost useless for us: 34608e14bc1SMauro Carvalho Chehab # any warning makes it non-zero. For kernel doc builds it always return 34708e14bc1SMauro Carvalho Chehab # non-zero even when build succeeds. So, let's do the best next thing: 34808e14bc1SMauro Carvalho Chehab # Ignore build errors. At the end, check if all PDF files were built, 34908e14bc1SMauro Carvalho Chehab # printing a summary with the built ones and returning 0 if all of 35008e14bc1SMauro Carvalho Chehab # them were actually built. 35108e14bc1SMauro Carvalho Chehab # 35208e14bc1SMauro Carvalho Chehab with futures.ThreadPoolExecutor(max_workers=n_jobs) as executor: 35308e14bc1SMauro Carvalho Chehab jobs = {} 35408e14bc1SMauro Carvalho Chehab 35508e14bc1SMauro Carvalho Chehab for from_dir, pdf_dir, entry in tex_files: 35608e14bc1SMauro Carvalho Chehab name = entry.name 35708e14bc1SMauro Carvalho Chehab 35808e14bc1SMauro Carvalho Chehab if not name.endswith(tex_suffix): 35908e14bc1SMauro Carvalho Chehab continue 36008e14bc1SMauro Carvalho Chehab 36108e14bc1SMauro Carvalho Chehab name = name[:-len(tex_suffix)] 36208e14bc1SMauro Carvalho Chehab has_tex = True 36308e14bc1SMauro Carvalho Chehab 36408e14bc1SMauro Carvalho Chehab future = executor.submit(self.build_pdf_file, latex_cmd, 36508e14bc1SMauro Carvalho Chehab from_dir, entry.path) 36608e14bc1SMauro Carvalho Chehab jobs[future] = (from_dir, pdf_dir, name) 36708e14bc1SMauro Carvalho Chehab 36808e14bc1SMauro Carvalho Chehab for future in futures.as_completed(jobs): 36908e14bc1SMauro Carvalho Chehab from_dir, pdf_dir, name = jobs[future] 37008e14bc1SMauro Carvalho Chehab 37108e14bc1SMauro Carvalho Chehab pdf_name = name + ".pdf" 37208e14bc1SMauro Carvalho Chehab pdf_from = os.path.join(from_dir, pdf_name) 3730d9abc76SMauro Carvalho Chehab pdf_to = os.path.join(pdf_dir, pdf_name) 3740d9abc76SMauro Carvalho Chehab out_name = os.path.relpath(pdf_to, self.builddir) 3750d9abc76SMauro Carvalho Chehab max_len = max(max_len, len(out_name)) 37608e14bc1SMauro Carvalho Chehab 37708e14bc1SMauro Carvalho Chehab try: 37808e14bc1SMauro Carvalho Chehab success = future.result() 37908e14bc1SMauro Carvalho Chehab 38008e14bc1SMauro Carvalho Chehab if success and os.path.exists(pdf_from): 38108e14bc1SMauro Carvalho Chehab os.rename(pdf_from, pdf_to) 38208e14bc1SMauro Carvalho Chehab 38308e14bc1SMauro Carvalho Chehab # 38408e14bc1SMauro Carvalho Chehab # if verbose, get the name of built PDF file 38508e14bc1SMauro Carvalho Chehab # 38608e14bc1SMauro Carvalho Chehab if self.verbose: 3870d9abc76SMauro Carvalho Chehab builds[out_name] = "SUCCESS" 38808e14bc1SMauro Carvalho Chehab else: 3890d9abc76SMauro Carvalho Chehab builds[out_name] = "FAILED" 39008e14bc1SMauro Carvalho Chehab build_failed = True 39108e14bc1SMauro Carvalho Chehab except futures.Error as e: 3920d9abc76SMauro Carvalho Chehab builds[out_name] = f"FAILED ({repr(e)})" 39308e14bc1SMauro Carvalho Chehab build_failed = True 39408e14bc1SMauro Carvalho Chehab 39508e14bc1SMauro Carvalho Chehab # 39608e14bc1SMauro Carvalho Chehab # Handle case where no .tex files were found 39708e14bc1SMauro Carvalho Chehab # 39808e14bc1SMauro Carvalho Chehab if not has_tex: 3990d9abc76SMauro Carvalho Chehab out_name = "LaTeX files" 4000d9abc76SMauro Carvalho Chehab max_len = max(max_len, len(out_name)) 4010d9abc76SMauro Carvalho Chehab builds[out_name] = "FAILED: no .tex files were generated" 40208e14bc1SMauro Carvalho Chehab build_failed = True 40308e14bc1SMauro Carvalho Chehab 40408e14bc1SMauro Carvalho Chehab return builds, build_failed, max_len 40508e14bc1SMauro Carvalho Chehab 406819667bcSMauro Carvalho Chehab def handle_pdf(self, output_dirs, deny_vf): 407819667bcSMauro Carvalho Chehab """ 408819667bcSMauro Carvalho Chehab Extra steps for PDF output. 409819667bcSMauro Carvalho Chehab 410819667bcSMauro Carvalho Chehab As PDF is handled via a LaTeX output, after building the .tex file, 411819667bcSMauro Carvalho Chehab a new build is needed to create the PDF output from the latex 412819667bcSMauro Carvalho Chehab directory. 413819667bcSMauro Carvalho Chehab """ 414819667bcSMauro Carvalho Chehab builds = {} 415819667bcSMauro Carvalho Chehab max_len = 0 41608e14bc1SMauro Carvalho Chehab tex_suffix = ".tex" 41708e14bc1SMauro Carvalho Chehab tex_files = [] 418819667bcSMauro Carvalho Chehab 419819667bcSMauro Carvalho Chehab # 420819667bcSMauro Carvalho Chehab # Since early 2024, Fedora and openSUSE tumbleweed have started 421819667bcSMauro Carvalho Chehab # deploying variable-font format of "Noto CJK", causing LaTeX 422819667bcSMauro Carvalho Chehab # to break with CJK. Work around it, by denying the variable font 423819667bcSMauro Carvalho Chehab # usage during xelatex build by passing the location of a config 424819667bcSMauro Carvalho Chehab # file with a deny list. 425819667bcSMauro Carvalho Chehab # 426819667bcSMauro Carvalho Chehab # See tools/docs/lib/latex_fonts.py for more details. 427819667bcSMauro Carvalho Chehab # 428819667bcSMauro Carvalho Chehab if deny_vf: 429819667bcSMauro Carvalho Chehab deny_vf = os.path.expanduser(deny_vf) 430819667bcSMauro Carvalho Chehab if os.path.isdir(deny_vf): 431819667bcSMauro Carvalho Chehab self.env["XDG_CONFIG_HOME"] = deny_vf 432819667bcSMauro Carvalho Chehab 433819667bcSMauro Carvalho Chehab for from_dir in output_dirs: 434819667bcSMauro Carvalho Chehab pdf_dir = os.path.join(from_dir, "../pdf") 435819667bcSMauro Carvalho Chehab os.makedirs(pdf_dir, exist_ok=True) 436819667bcSMauro Carvalho Chehab 437819667bcSMauro Carvalho Chehab if self.latexmk_cmd: 438819667bcSMauro Carvalho Chehab latex_cmd = [self.latexmk_cmd, f"-{self.pdflatex}"] 439819667bcSMauro Carvalho Chehab else: 440819667bcSMauro Carvalho Chehab latex_cmd = [self.pdflatex] 441819667bcSMauro Carvalho Chehab 442819667bcSMauro Carvalho Chehab latex_cmd.extend(shlex.split(self.latexopts)) 443819667bcSMauro Carvalho Chehab 44408e14bc1SMauro Carvalho Chehab # Get a list of tex files to process 445819667bcSMauro Carvalho Chehab with os.scandir(from_dir) as it: 446819667bcSMauro Carvalho Chehab for entry in it: 44708e14bc1SMauro Carvalho Chehab if entry.name.endswith(tex_suffix): 44808e14bc1SMauro Carvalho Chehab tex_files.append((from_dir, pdf_dir, entry)) 449819667bcSMauro Carvalho Chehab 450819667bcSMauro Carvalho Chehab # 45108e14bc1SMauro Carvalho Chehab # When using make, this won't be used, as the number of jobs comes 45208e14bc1SMauro Carvalho Chehab # from POSIX jobserver. So, this covers the case where build comes 45308e14bc1SMauro Carvalho Chehab # from command line. On such case, serialize by default, except if 45408e14bc1SMauro Carvalho Chehab # the user explicitly sets the number of jobs. 455819667bcSMauro Carvalho Chehab # 45608e14bc1SMauro Carvalho Chehab n_jobs = 1 45708e14bc1SMauro Carvalho Chehab 45808e14bc1SMauro Carvalho Chehab # n_jobs is either an integer or "auto". Only use it if it is a number 45908e14bc1SMauro Carvalho Chehab if self.n_jobs: 460819667bcSMauro Carvalho Chehab try: 46108e14bc1SMauro Carvalho Chehab n_jobs = int(self.n_jobs) 46208e14bc1SMauro Carvalho Chehab except ValueError: 463819667bcSMauro Carvalho Chehab pass 464819667bcSMauro Carvalho Chehab 46508e14bc1SMauro Carvalho Chehab # 46608e14bc1SMauro Carvalho Chehab # When using make, jobserver.claim is the number of jobs that were 46708e14bc1SMauro Carvalho Chehab # used with "-j" and that aren't used by other make targets 46808e14bc1SMauro Carvalho Chehab # 46908e14bc1SMauro Carvalho Chehab with JobserverExec() as jobserver: 47008e14bc1SMauro Carvalho Chehab n_jobs = 1 471819667bcSMauro Carvalho Chehab 47208e14bc1SMauro Carvalho Chehab # 47308e14bc1SMauro Carvalho Chehab # Handle the case when a parameter is passed via command line, 47408e14bc1SMauro Carvalho Chehab # using it as default, if jobserver doesn't claim anything 47508e14bc1SMauro Carvalho Chehab # 47608e14bc1SMauro Carvalho Chehab if self.n_jobs: 47708e14bc1SMauro Carvalho Chehab try: 47808e14bc1SMauro Carvalho Chehab n_jobs = int(self.n_jobs) 47908e14bc1SMauro Carvalho Chehab except ValueError: 48008e14bc1SMauro Carvalho Chehab pass 481819667bcSMauro Carvalho Chehab 48208e14bc1SMauro Carvalho Chehab if jobserver.claim: 48308e14bc1SMauro Carvalho Chehab n_jobs = jobserver.claim 484819667bcSMauro Carvalho Chehab 48508e14bc1SMauro Carvalho Chehab builds, build_failed, max_len = self.pdf_parallel_build(tex_suffix, 48608e14bc1SMauro Carvalho Chehab latex_cmd, 48708e14bc1SMauro Carvalho Chehab tex_files, 48808e14bc1SMauro Carvalho Chehab n_jobs) 489819667bcSMauro Carvalho Chehab 49008e14bc1SMauro Carvalho Chehab # 49108e14bc1SMauro Carvalho Chehab # In verbose mode, print a summary with the build results per file. 49208e14bc1SMauro Carvalho Chehab # Otherwise, print a single line with all failures, if any. 49308e14bc1SMauro Carvalho Chehab # On both cases, return code 1 indicates build failures, 49408e14bc1SMauro Carvalho Chehab # 49508e14bc1SMauro Carvalho Chehab if self.verbose: 496819667bcSMauro Carvalho Chehab msg = "Summary" 497819667bcSMauro Carvalho Chehab msg += "\n" + "=" * len(msg) 498819667bcSMauro Carvalho Chehab print() 499819667bcSMauro Carvalho Chehab print(msg) 500819667bcSMauro Carvalho Chehab 501819667bcSMauro Carvalho Chehab for pdf_name, pdf_file in builds.items(): 502819667bcSMauro Carvalho Chehab print(f"{pdf_name:<{max_len}}: {pdf_file}") 503819667bcSMauro Carvalho Chehab 504819667bcSMauro Carvalho Chehab print() 505819667bcSMauro Carvalho Chehab if build_failed: 506819667bcSMauro Carvalho Chehab msg = LatexFontChecker().check() 507819667bcSMauro Carvalho Chehab if msg: 508819667bcSMauro Carvalho Chehab print(msg) 509819667bcSMauro Carvalho Chehab 51008e14bc1SMauro Carvalho Chehab sys.exit("Error: not all PDF files were created.") 51108e14bc1SMauro Carvalho Chehab 51208e14bc1SMauro Carvalho Chehab elif build_failed: 51308e14bc1SMauro Carvalho Chehab n_failures = len(builds) 51408e14bc1SMauro Carvalho Chehab failures = ", ".join(builds.keys()) 51508e14bc1SMauro Carvalho Chehab 51608e14bc1SMauro Carvalho Chehab msg = LatexFontChecker().check() 51708e14bc1SMauro Carvalho Chehab if msg: 51808e14bc1SMauro Carvalho Chehab print(msg) 51908e14bc1SMauro Carvalho Chehab 52008e14bc1SMauro Carvalho Chehab sys.exit(f"Error: Can't build {n_failures} PDF file(s): {failures}") 521819667bcSMauro Carvalho Chehab 522819667bcSMauro Carvalho Chehab def handle_info(self, output_dirs): 523819667bcSMauro Carvalho Chehab """ 524819667bcSMauro Carvalho Chehab Extra steps for Info output. 525819667bcSMauro Carvalho Chehab 526819667bcSMauro Carvalho Chehab For texinfo generation, an additional make is needed from the 527819667bcSMauro Carvalho Chehab texinfo directory. 528819667bcSMauro Carvalho Chehab """ 529819667bcSMauro Carvalho Chehab 530819667bcSMauro Carvalho Chehab for output_dir in output_dirs: 531819667bcSMauro Carvalho Chehab try: 532819667bcSMauro Carvalho Chehab subprocess.run(["make", "info"], cwd=output_dir, check=True) 533819667bcSMauro Carvalho Chehab except subprocess.CalledProcessError as e: 534819667bcSMauro Carvalho Chehab sys.exit(f"Error generating info docs: {e}") 535819667bcSMauro Carvalho Chehab 5367e8a8143SMauro Carvalho Chehab def handle_man(self, kerneldoc, docs_dir, src_dir, output_dir): 5377e8a8143SMauro Carvalho Chehab """ 5387e8a8143SMauro Carvalho Chehab Create man pages from kernel-doc output 5397e8a8143SMauro Carvalho Chehab """ 5407e8a8143SMauro Carvalho Chehab 5417e8a8143SMauro Carvalho Chehab re_kernel_doc = re.compile(r"^\.\.\s+kernel-doc::\s*(\S+)") 5427e8a8143SMauro Carvalho Chehab re_man = re.compile(r'^\.TH "[^"]*" (\d+) "([^"]*)"') 5437e8a8143SMauro Carvalho Chehab 5447e8a8143SMauro Carvalho Chehab if docs_dir == src_dir: 5457e8a8143SMauro Carvalho Chehab # 5467e8a8143SMauro Carvalho Chehab # Pick the entire set of kernel-doc markups from the entire tree 5477e8a8143SMauro Carvalho Chehab # 5487e8a8143SMauro Carvalho Chehab kdoc_files = set([self.srctree]) 5497e8a8143SMauro Carvalho Chehab else: 5507e8a8143SMauro Carvalho Chehab kdoc_files = set() 5517e8a8143SMauro Carvalho Chehab 5527e8a8143SMauro Carvalho Chehab for fname in glob(os.path.join(src_dir, "**"), recursive=True): 5537e8a8143SMauro Carvalho Chehab if os.path.isfile(fname) and fname.endswith(".rst"): 5547e8a8143SMauro Carvalho Chehab with open(fname, "r", encoding="utf-8") as in_fp: 5557e8a8143SMauro Carvalho Chehab data = in_fp.read() 5567e8a8143SMauro Carvalho Chehab 5577e8a8143SMauro Carvalho Chehab for line in data.split("\n"): 5587e8a8143SMauro Carvalho Chehab match = re_kernel_doc.match(line) 5597e8a8143SMauro Carvalho Chehab if match: 5607e8a8143SMauro Carvalho Chehab if os.path.isfile(match.group(1)): 5617e8a8143SMauro Carvalho Chehab kdoc_files.add(match.group(1)) 5627e8a8143SMauro Carvalho Chehab 5637e8a8143SMauro Carvalho Chehab if not kdoc_files: 5647e8a8143SMauro Carvalho Chehab sys.exit(f"Directory {src_dir} doesn't contain kernel-doc tags") 5657e8a8143SMauro Carvalho Chehab 5667e8a8143SMauro Carvalho Chehab cmd = [ kerneldoc, "-m" ] + sorted(kdoc_files) 5677e8a8143SMauro Carvalho Chehab try: 5687e8a8143SMauro Carvalho Chehab if self.verbose: 5697e8a8143SMauro Carvalho Chehab print(" ".join(cmd)) 5707e8a8143SMauro Carvalho Chehab 5717e8a8143SMauro Carvalho Chehab result = subprocess.run(cmd, stdout=subprocess.PIPE, text= True) 5727e8a8143SMauro Carvalho Chehab 5737e8a8143SMauro Carvalho Chehab if result.returncode: 5747e8a8143SMauro Carvalho Chehab print(f"Warning: kernel-doc returned {result.returncode} warnings") 5757e8a8143SMauro Carvalho Chehab 5767e8a8143SMauro Carvalho Chehab except (OSError, ValueError, subprocess.SubprocessError) as e: 5777e8a8143SMauro Carvalho Chehab sys.exit(f"Failed to create man pages for {src_dir}: {repr(e)}") 5787e8a8143SMauro Carvalho Chehab 5797e8a8143SMauro Carvalho Chehab fp = None 5807e8a8143SMauro Carvalho Chehab try: 5817e8a8143SMauro Carvalho Chehab for line in result.stdout.split("\n"): 5827e8a8143SMauro Carvalho Chehab match = re_man.match(line) 5837e8a8143SMauro Carvalho Chehab if not match: 5847e8a8143SMauro Carvalho Chehab if fp: 5857e8a8143SMauro Carvalho Chehab fp.write(line + '\n') 5867e8a8143SMauro Carvalho Chehab continue 5877e8a8143SMauro Carvalho Chehab 5887e8a8143SMauro Carvalho Chehab if fp: 5897e8a8143SMauro Carvalho Chehab fp.close() 5907e8a8143SMauro Carvalho Chehab 5917e8a8143SMauro Carvalho Chehab fname = f"{output_dir}/{match.group(2)}.{match.group(1)}" 5927e8a8143SMauro Carvalho Chehab 5937e8a8143SMauro Carvalho Chehab if self.verbose: 5947e8a8143SMauro Carvalho Chehab print(f"Creating {fname}") 5957e8a8143SMauro Carvalho Chehab fp = open(fname, "w", encoding="utf-8") 5967e8a8143SMauro Carvalho Chehab fp.write(line + '\n') 5977e8a8143SMauro Carvalho Chehab finally: 5987e8a8143SMauro Carvalho Chehab if fp: 5997e8a8143SMauro Carvalho Chehab fp.close() 6007e8a8143SMauro Carvalho Chehab 601819667bcSMauro Carvalho Chehab def cleandocs(self, builder): # pylint: disable=W0613 602819667bcSMauro Carvalho Chehab """Remove documentation output directory""" 603819667bcSMauro Carvalho Chehab shutil.rmtree(self.builddir, ignore_errors=True) 604819667bcSMauro Carvalho Chehab 605819667bcSMauro Carvalho Chehab def build(self, target, sphinxdirs=None, conf="conf.py", 6062118ba7dSMauro Carvalho Chehab theme=None, css=None, paper=None, deny_vf=None, rustdoc=False): 607819667bcSMauro Carvalho Chehab """ 608819667bcSMauro Carvalho Chehab Build documentation using Sphinx. This is the core function of this 609819667bcSMauro Carvalho Chehab module. It prepares all arguments required by sphinx-build. 610819667bcSMauro Carvalho Chehab """ 611819667bcSMauro Carvalho Chehab 612819667bcSMauro Carvalho Chehab builder = TARGETS[target]["builder"] 613819667bcSMauro Carvalho Chehab out_dir = TARGETS[target].get("out_dir", "") 614819667bcSMauro Carvalho Chehab 615819667bcSMauro Carvalho Chehab # 616819667bcSMauro Carvalho Chehab # Cleandocs doesn't require sphinx-build 617819667bcSMauro Carvalho Chehab # 618819667bcSMauro Carvalho Chehab if target == "cleandocs": 619819667bcSMauro Carvalho Chehab self.cleandocs(builder) 620819667bcSMauro Carvalho Chehab return 621819667bcSMauro Carvalho Chehab 622819667bcSMauro Carvalho Chehab if theme: 623819667bcSMauro Carvalho Chehab os.environ["DOCS_THEME"] = theme 624819667bcSMauro Carvalho Chehab 625819667bcSMauro Carvalho Chehab # 626819667bcSMauro Carvalho Chehab # Other targets require sphinx-build, so check if it exists 627819667bcSMauro Carvalho Chehab # 628819667bcSMauro Carvalho Chehab sphinxbuild = shutil.which(self.sphinxbuild, path=self.env["PATH"]) 6297e8a8143SMauro Carvalho Chehab if not sphinxbuild and target != "mandocs": 630819667bcSMauro Carvalho Chehab sys.exit(f"Error: {self.sphinxbuild} not found in PATH.\n") 631819667bcSMauro Carvalho Chehab 632819667bcSMauro Carvalho Chehab if builder == "latex": 633819667bcSMauro Carvalho Chehab if not self.pdflatex_cmd and not self.latexmk_cmd: 634819667bcSMauro Carvalho Chehab sys.exit("Error: pdflatex or latexmk required for PDF generation") 635819667bcSMauro Carvalho Chehab 636819667bcSMauro Carvalho Chehab docs_dir = os.path.abspath(os.path.join(self.srctree, "Documentation")) 637819667bcSMauro Carvalho Chehab 638819667bcSMauro Carvalho Chehab # 639819667bcSMauro Carvalho Chehab # Fill in base arguments for Sphinx build 640819667bcSMauro Carvalho Chehab # 641819667bcSMauro Carvalho Chehab kerneldoc = self.kerneldoc 642819667bcSMauro Carvalho Chehab if kerneldoc.startswith(self.srctree): 643819667bcSMauro Carvalho Chehab kerneldoc = os.path.relpath(kerneldoc, self.srctree) 644819667bcSMauro Carvalho Chehab 645819667bcSMauro Carvalho Chehab args = [ "-b", builder, "-c", docs_dir ] 646819667bcSMauro Carvalho Chehab 647819667bcSMauro Carvalho Chehab if builder == "latex": 648819667bcSMauro Carvalho Chehab if not paper: 649819667bcSMauro Carvalho Chehab paper = PAPER[1] 650819667bcSMauro Carvalho Chehab 651819667bcSMauro Carvalho Chehab args.extend(["-D", f"latex_elements.papersize={paper}paper"]) 652819667bcSMauro Carvalho Chehab 6532118ba7dSMauro Carvalho Chehab if rustdoc: 654819667bcSMauro Carvalho Chehab args.extend(["-t", "rustdoc"]) 655819667bcSMauro Carvalho Chehab 656819667bcSMauro Carvalho Chehab if conf: 657819667bcSMauro Carvalho Chehab self.env["SPHINX_CONF"] = self.get_path(conf, abs_path=True) 658819667bcSMauro Carvalho Chehab 659819667bcSMauro Carvalho Chehab if not sphinxdirs: 660819667bcSMauro Carvalho Chehab sphinxdirs = os.environ.get("SPHINXDIRS", ".") 661819667bcSMauro Carvalho Chehab 662819667bcSMauro Carvalho Chehab # 66382c294d4SMauro Carvalho Chehab # The sphinx-build tool has a bug: internally, it tries to set 66482c294d4SMauro Carvalho Chehab # locale with locale.setlocale(locale.LC_ALL, ''). This causes a 66582c294d4SMauro Carvalho Chehab # crash if language is not set. Detect and fix it. 66682c294d4SMauro Carvalho Chehab # 66782c294d4SMauro Carvalho Chehab try: 66882c294d4SMauro Carvalho Chehab locale.setlocale(locale.LC_ALL, '') 66982c294d4SMauro Carvalho Chehab except locale.Error: 67082c294d4SMauro Carvalho Chehab self.env["LC_ALL"] = "C" 67182c294d4SMauro Carvalho Chehab 67282c294d4SMauro Carvalho Chehab # 673819667bcSMauro Carvalho Chehab # sphinxdirs can be a list or a whitespace-separated string 674819667bcSMauro Carvalho Chehab # 675819667bcSMauro Carvalho Chehab sphinxdirs_list = [] 676819667bcSMauro Carvalho Chehab for sphinxdir in sphinxdirs: 677819667bcSMauro Carvalho Chehab if isinstance(sphinxdir, list): 678819667bcSMauro Carvalho Chehab sphinxdirs_list += sphinxdir 679819667bcSMauro Carvalho Chehab else: 680819667bcSMauro Carvalho Chehab sphinxdirs_list += sphinxdir.split() 681819667bcSMauro Carvalho Chehab 682819667bcSMauro Carvalho Chehab # 683819667bcSMauro Carvalho Chehab # Step 1: Build each directory in separate. 684819667bcSMauro Carvalho Chehab # 685819667bcSMauro Carvalho Chehab # This is not the best way of handling it, as cross-references between 686819667bcSMauro Carvalho Chehab # them will be broken, but this is what we've been doing since 687819667bcSMauro Carvalho Chehab # the beginning. 688819667bcSMauro Carvalho Chehab # 689819667bcSMauro Carvalho Chehab output_dirs = [] 690819667bcSMauro Carvalho Chehab for sphinxdir in sphinxdirs_list: 691819667bcSMauro Carvalho Chehab src_dir = os.path.join(docs_dir, sphinxdir) 692819667bcSMauro Carvalho Chehab doctree_dir = os.path.join(self.builddir, ".doctrees") 693819667bcSMauro Carvalho Chehab output_dir = os.path.join(self.builddir, sphinxdir, out_dir) 694819667bcSMauro Carvalho Chehab 695819667bcSMauro Carvalho Chehab # 696819667bcSMauro Carvalho Chehab # Make directory names canonical 697819667bcSMauro Carvalho Chehab # 698819667bcSMauro Carvalho Chehab src_dir = os.path.normpath(src_dir) 699819667bcSMauro Carvalho Chehab doctree_dir = os.path.normpath(doctree_dir) 700819667bcSMauro Carvalho Chehab output_dir = os.path.normpath(output_dir) 701819667bcSMauro Carvalho Chehab 702819667bcSMauro Carvalho Chehab os.makedirs(doctree_dir, exist_ok=True) 703819667bcSMauro Carvalho Chehab os.makedirs(output_dir, exist_ok=True) 704819667bcSMauro Carvalho Chehab 705819667bcSMauro Carvalho Chehab output_dirs.append(output_dir) 706819667bcSMauro Carvalho Chehab 707819667bcSMauro Carvalho Chehab build_args = args + [ 708819667bcSMauro Carvalho Chehab "-d", doctree_dir, 709819667bcSMauro Carvalho Chehab "-D", f"kerneldoc_bin={kerneldoc}", 710819667bcSMauro Carvalho Chehab "-D", f"version={self.kernelversion}", 711819667bcSMauro Carvalho Chehab "-D", f"release={self.kernelrelease}", 712819667bcSMauro Carvalho Chehab "-D", f"kerneldoc_srctree={self.srctree}", 713819667bcSMauro Carvalho Chehab src_dir, 714819667bcSMauro Carvalho Chehab output_dir, 715819667bcSMauro Carvalho Chehab ] 716819667bcSMauro Carvalho Chehab 7177e8a8143SMauro Carvalho Chehab if target == "mandocs": 7187e8a8143SMauro Carvalho Chehab self.handle_man(kerneldoc, docs_dir, src_dir, output_dir) 7197e8a8143SMauro Carvalho Chehab else: 720819667bcSMauro Carvalho Chehab try: 721819667bcSMauro Carvalho Chehab self.run_sphinx(sphinxbuild, build_args, env=self.env) 722819667bcSMauro Carvalho Chehab except (OSError, ValueError, subprocess.SubprocessError) as e: 723819667bcSMauro Carvalho Chehab sys.exit(f"Build failed: {repr(e)}") 724819667bcSMauro Carvalho Chehab 725819667bcSMauro Carvalho Chehab # 726819667bcSMauro Carvalho Chehab # Ensure that each html/epub output will have needed static files 727819667bcSMauro Carvalho Chehab # 728819667bcSMauro Carvalho Chehab if target in ["htmldocs", "epubdocs"]: 7292118ba7dSMauro Carvalho Chehab self.handle_html(css, output_dir, rustdoc) 730819667bcSMauro Carvalho Chehab 731819667bcSMauro Carvalho Chehab # 732819667bcSMauro Carvalho Chehab # Step 2: Some targets (PDF and info) require an extra step once 733819667bcSMauro Carvalho Chehab # sphinx-build finishes 734819667bcSMauro Carvalho Chehab # 735819667bcSMauro Carvalho Chehab if target == "pdfdocs": 736819667bcSMauro Carvalho Chehab self.handle_pdf(output_dirs, deny_vf) 737819667bcSMauro Carvalho Chehab elif target == "infodocs": 738819667bcSMauro Carvalho Chehab self.handle_info(output_dirs) 739819667bcSMauro Carvalho Chehab 740819667bcSMauro Carvalho Chehabdef jobs_type(value): 741819667bcSMauro Carvalho Chehab """ 742819667bcSMauro Carvalho Chehab Handle valid values for -j. Accepts Sphinx "-jauto", plus a number 743819667bcSMauro Carvalho Chehab equal or bigger than one. 744819667bcSMauro Carvalho Chehab """ 745819667bcSMauro Carvalho Chehab if value is None: 746819667bcSMauro Carvalho Chehab return None 747819667bcSMauro Carvalho Chehab 748819667bcSMauro Carvalho Chehab if value.lower() == 'auto': 749819667bcSMauro Carvalho Chehab return value.lower() 750819667bcSMauro Carvalho Chehab 751819667bcSMauro Carvalho Chehab try: 752819667bcSMauro Carvalho Chehab if int(value) >= 1: 753819667bcSMauro Carvalho Chehab return value 754819667bcSMauro Carvalho Chehab 755819667bcSMauro Carvalho Chehab raise argparse.ArgumentTypeError(f"Minimum jobs is 1, got {value}") 756819667bcSMauro Carvalho Chehab except ValueError: 757819667bcSMauro Carvalho Chehab raise argparse.ArgumentTypeError(f"Must be 'auto' or positive integer, got {value}") # pylint: disable=W0707 758819667bcSMauro Carvalho Chehab 759819667bcSMauro Carvalho Chehabdef main(): 760819667bcSMauro Carvalho Chehab """ 761819667bcSMauro Carvalho Chehab Main function. The only mandatory argument is the target. If not 762819667bcSMauro Carvalho Chehab specified, the other arguments will use default values if not 763819667bcSMauro Carvalho Chehab specified at os.environ. 764819667bcSMauro Carvalho Chehab """ 765819667bcSMauro Carvalho Chehab parser = argparse.ArgumentParser(description="Kernel documentation builder") 766819667bcSMauro Carvalho Chehab 767819667bcSMauro Carvalho Chehab parser.add_argument("target", choices=list(TARGETS.keys()), 768819667bcSMauro Carvalho Chehab help="Documentation target to build") 769819667bcSMauro Carvalho Chehab parser.add_argument("--sphinxdirs", nargs="+", 770819667bcSMauro Carvalho Chehab help="Specific directories to build") 771819667bcSMauro Carvalho Chehab parser.add_argument("--conf", default="conf.py", 772819667bcSMauro Carvalho Chehab help="Sphinx configuration file") 773819667bcSMauro Carvalho Chehab parser.add_argument("--builddir", default="output", 774819667bcSMauro Carvalho Chehab help="Sphinx configuration file") 775819667bcSMauro Carvalho Chehab 776819667bcSMauro Carvalho Chehab parser.add_argument("--theme", help="Sphinx theme to use") 777819667bcSMauro Carvalho Chehab 778819667bcSMauro Carvalho Chehab parser.add_argument("--css", help="Custom CSS file for HTML/EPUB") 779819667bcSMauro Carvalho Chehab 780819667bcSMauro Carvalho Chehab parser.add_argument("--paper", choices=PAPER, default=PAPER[0], 781819667bcSMauro Carvalho Chehab help="Paper size for LaTeX/PDF output") 782819667bcSMauro Carvalho Chehab 783819667bcSMauro Carvalho Chehab parser.add_argument('--deny-vf', 784819667bcSMauro Carvalho Chehab help="Configuration to deny variable fonts on pdf builds") 785819667bcSMauro Carvalho Chehab 7862118ba7dSMauro Carvalho Chehab parser.add_argument('--rustdoc', action="store_true", 7872118ba7dSMauro Carvalho Chehab help="Enable rustdoc build. Requires CONFIG_RUST") 7882118ba7dSMauro Carvalho Chehab 789819667bcSMauro Carvalho Chehab parser.add_argument("-v", "--verbose", action='store_true', 790819667bcSMauro Carvalho Chehab help="place build in verbose mode") 791819667bcSMauro Carvalho Chehab 792819667bcSMauro Carvalho Chehab parser.add_argument('-j', '--jobs', type=jobs_type, 793819667bcSMauro Carvalho Chehab help="Sets number of jobs to use with sphinx-build") 794819667bcSMauro Carvalho Chehab 7952f99b85eSMauro Carvalho Chehab parser.add_argument('-i', '--interactive', action='store_true', 7962f99b85eSMauro Carvalho Chehab help="Change latex default to run in interactive mode") 7972f99b85eSMauro Carvalho Chehab 798*42180adaSMauro Carvalho Chehab parser.add_argument("-V", "--venv", nargs='?', const=f'{VENV_DEFAULT}', 799*42180adaSMauro Carvalho Chehab default=None, 800*42180adaSMauro Carvalho Chehab help=f'If used, run Sphinx from a venv dir (default dir: {VENV_DEFAULT})') 801*42180adaSMauro Carvalho Chehab 802819667bcSMauro Carvalho Chehab args = parser.parse_args() 803819667bcSMauro Carvalho Chehab 80462ea383bSMauro Carvalho Chehab PythonVersion.check_python(MIN_PYTHON_VERSION, show_alternatives=True, 80562ea383bSMauro Carvalho Chehab bail_out=True) 806819667bcSMauro Carvalho Chehab 807*42180adaSMauro Carvalho Chehab builder = SphinxBuilder(builddir=args.builddir, venv=args.venv, 8082f99b85eSMauro Carvalho Chehab verbose=args.verbose, n_jobs=args.jobs, 8092f99b85eSMauro Carvalho Chehab interactive=args.interactive) 810819667bcSMauro Carvalho Chehab 811819667bcSMauro Carvalho Chehab builder.build(args.target, sphinxdirs=args.sphinxdirs, conf=args.conf, 812819667bcSMauro Carvalho Chehab theme=args.theme, css=args.css, paper=args.paper, 8132118ba7dSMauro Carvalho Chehab rustdoc=args.rustdoc, deny_vf=args.deny_vf) 814819667bcSMauro Carvalho Chehab 815819667bcSMauro Carvalho Chehabif __name__ == "__main__": 816819667bcSMauro Carvalho Chehab main() 817