xref: /linux/tools/docs/sphinx-build-wrapper (revision 0d9abc7627f5aeaaa8db6e856d800819cc9d85b1)
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
50819667bcSMauro Carvalho Chehabimport shlex
51819667bcSMauro Carvalho Chehabimport shutil
52819667bcSMauro Carvalho Chehabimport subprocess
53819667bcSMauro Carvalho Chehabimport sys
54819667bcSMauro Carvalho Chehab
5508e14bc1SMauro Carvalho Chehabfrom concurrent import futures
5608e14bc1SMauro Carvalho Chehab
57819667bcSMauro Carvalho Chehabfrom lib.python_version import PythonVersion
58819667bcSMauro Carvalho Chehabfrom lib.latex_fonts import LatexFontChecker
59819667bcSMauro Carvalho Chehab
60819667bcSMauro Carvalho ChehabLIB_DIR = "../../scripts/lib"
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
65819667bcSMauro Carvalho Chehabfrom jobserver import JobserverExec         # pylint: disable=C0413,C0411,E0401
66819667bcSMauro Carvalho Chehab
67819667bcSMauro Carvalho Chehab#
68819667bcSMauro Carvalho Chehab#  Some constants
69819667bcSMauro Carvalho Chehab#
70819667bcSMauro Carvalho ChehabMIN_PYTHON_VERSION = PythonVersion("3.7").version
71819667bcSMauro Carvalho ChehabPAPER = ["", "a4", "letter"]
72819667bcSMauro Carvalho Chehab
73819667bcSMauro Carvalho ChehabTARGETS = {
74819667bcSMauro Carvalho Chehab    "cleandocs":     { "builder": "clean" },
75819667bcSMauro Carvalho Chehab    "linkcheckdocs": { "builder": "linkcheck" },
76819667bcSMauro Carvalho Chehab    "htmldocs":      { "builder": "html" },
77819667bcSMauro Carvalho Chehab    "epubdocs":      { "builder": "epub",    "out_dir": "epub" },
78819667bcSMauro Carvalho Chehab    "texinfodocs":   { "builder": "texinfo", "out_dir": "texinfo" },
79819667bcSMauro Carvalho Chehab    "infodocs":      { "builder": "texinfo", "out_dir": "texinfo" },
80819667bcSMauro Carvalho Chehab    "latexdocs":     { "builder": "latex",   "out_dir": "latex" },
81819667bcSMauro Carvalho Chehab    "pdfdocs":       { "builder": "latex",   "out_dir": "latex" },
82819667bcSMauro Carvalho Chehab    "xmldocs":       { "builder": "xml",     "out_dir": "xml" },
83819667bcSMauro Carvalho Chehab}
84819667bcSMauro Carvalho Chehab
85819667bcSMauro Carvalho Chehab
86819667bcSMauro Carvalho Chehab#
87819667bcSMauro Carvalho Chehab# SphinxBuilder class
88819667bcSMauro Carvalho Chehab#
89819667bcSMauro Carvalho Chehab
90819667bcSMauro Carvalho Chehabclass SphinxBuilder:
91819667bcSMauro Carvalho Chehab    """
92819667bcSMauro Carvalho Chehab    Handles a sphinx-build target, adding needed arguments to build
93819667bcSMauro Carvalho Chehab    with the Kernel.
94819667bcSMauro Carvalho Chehab    """
95819667bcSMauro Carvalho Chehab
96819667bcSMauro Carvalho Chehab    def is_rust_enabled(self):
97819667bcSMauro Carvalho Chehab        """Check if rust is enabled at .config"""
98819667bcSMauro Carvalho Chehab        config_path = os.path.join(self.srctree, ".config")
99819667bcSMauro Carvalho Chehab        if os.path.isfile(config_path):
100819667bcSMauro Carvalho Chehab            with open(config_path, "r", encoding="utf-8") as f:
101819667bcSMauro Carvalho Chehab                return "CONFIG_RUST=y" in f.read()
102819667bcSMauro Carvalho Chehab        return False
103819667bcSMauro Carvalho Chehab
104819667bcSMauro Carvalho Chehab    def get_path(self, path, use_cwd=False, abs_path=False):
105819667bcSMauro Carvalho Chehab        """
106819667bcSMauro Carvalho Chehab        Ancillary routine to handle patches the right way, as shell does.
107819667bcSMauro Carvalho Chehab
108819667bcSMauro Carvalho Chehab        It first expands "~" and "~user". Then, if patch is not absolute,
109819667bcSMauro Carvalho Chehab        join self.srctree. Finally, if requested, convert to abspath.
110819667bcSMauro Carvalho Chehab        """
111819667bcSMauro Carvalho Chehab
112819667bcSMauro Carvalho Chehab        path = os.path.expanduser(path)
113819667bcSMauro Carvalho Chehab        if not path.startswith("/"):
114819667bcSMauro Carvalho Chehab            if use_cwd:
115819667bcSMauro Carvalho Chehab                base = os.getcwd()
116819667bcSMauro Carvalho Chehab            else:
117819667bcSMauro Carvalho Chehab                base = self.srctree
118819667bcSMauro Carvalho Chehab
119819667bcSMauro Carvalho Chehab            path = os.path.join(base, path)
120819667bcSMauro Carvalho Chehab
121819667bcSMauro Carvalho Chehab        if abs_path:
122819667bcSMauro Carvalho Chehab            return os.path.abspath(path)
123819667bcSMauro Carvalho Chehab
124819667bcSMauro Carvalho Chehab        return path
125819667bcSMauro Carvalho Chehab
126819667bcSMauro Carvalho Chehab    def get_sphinx_extra_opts(self, n_jobs):
127819667bcSMauro Carvalho Chehab        """
128819667bcSMauro Carvalho Chehab        Get the number of jobs to be used for docs build passed via command
129819667bcSMauro Carvalho Chehab        line and desired sphinx verbosity.
130819667bcSMauro Carvalho Chehab
131819667bcSMauro Carvalho Chehab        The number of jobs can be on different places:
132819667bcSMauro Carvalho Chehab
133819667bcSMauro Carvalho Chehab        1) It can be passed via "-j" argument;
134819667bcSMauro Carvalho Chehab        2) The SPHINXOPTS="-j8" env var may have "-j";
135819667bcSMauro Carvalho Chehab        3) if called via GNU make, -j specifies the desired number of jobs.
136819667bcSMauro Carvalho Chehab           with GNU makefile, this number is available via POSIX jobserver;
137819667bcSMauro Carvalho Chehab        4) if none of the above is available, it should default to "-jauto",
138819667bcSMauro Carvalho Chehab           and let sphinx decide the best value.
139819667bcSMauro Carvalho Chehab        """
140819667bcSMauro Carvalho Chehab
141819667bcSMauro Carvalho Chehab        #
142819667bcSMauro Carvalho Chehab        # SPHINXOPTS env var, if used, contains extra arguments to be used
143819667bcSMauro Carvalho Chehab        # by sphinx-build time. Among them, it may contain sphinx verbosity
144819667bcSMauro Carvalho Chehab        # and desired number of parallel jobs.
145819667bcSMauro Carvalho Chehab        #
146819667bcSMauro Carvalho Chehab        parser = argparse.ArgumentParser()
147819667bcSMauro Carvalho Chehab        parser.add_argument('-j', '--jobs', type=int)
148819667bcSMauro Carvalho Chehab        parser.add_argument('-q', '--quiet', type=int)
149819667bcSMauro Carvalho Chehab
150819667bcSMauro Carvalho Chehab        #
151819667bcSMauro Carvalho Chehab        # Other sphinx-build arguments go as-is, so place them
152819667bcSMauro Carvalho Chehab        # at self.sphinxopts, using shell parser
153819667bcSMauro Carvalho Chehab        #
154819667bcSMauro Carvalho Chehab        sphinxopts = shlex.split(os.environ.get("SPHINXOPTS", ""))
155819667bcSMauro Carvalho Chehab
156819667bcSMauro Carvalho Chehab        #
157819667bcSMauro Carvalho Chehab        # Build a list of sphinx args, honoring verbosity here if specified
158819667bcSMauro Carvalho Chehab        #
159819667bcSMauro Carvalho Chehab
160819667bcSMauro Carvalho Chehab        verbose = self.verbose
161819667bcSMauro Carvalho Chehab        sphinx_args, self.sphinxopts = parser.parse_known_args(sphinxopts)
162819667bcSMauro Carvalho Chehab        if sphinx_args.quiet is True:
163819667bcSMauro Carvalho Chehab            verbose = False
164819667bcSMauro Carvalho Chehab
165819667bcSMauro Carvalho Chehab        #
166819667bcSMauro Carvalho Chehab        # If the user explicitly sets "-j" at command line, use it.
167819667bcSMauro Carvalho Chehab        # Otherwise, pick it from SPHINXOPTS args
168819667bcSMauro Carvalho Chehab        #
169819667bcSMauro Carvalho Chehab        if n_jobs:
170819667bcSMauro Carvalho Chehab            self.n_jobs = n_jobs
171819667bcSMauro Carvalho Chehab        elif sphinx_args.jobs:
172819667bcSMauro Carvalho Chehab            self.n_jobs = sphinx_args.jobs
173819667bcSMauro Carvalho Chehab        else:
174819667bcSMauro Carvalho Chehab            self.n_jobs = None
175819667bcSMauro Carvalho Chehab
176819667bcSMauro Carvalho Chehab        if not verbose:
177819667bcSMauro Carvalho Chehab            self.sphinxopts += ["-q"]
178819667bcSMauro Carvalho Chehab
1792f99b85eSMauro Carvalho Chehab    def __init__(self, builddir, verbose=False, n_jobs=None, interactive=None):
180819667bcSMauro Carvalho Chehab        """Initialize internal variables"""
181819667bcSMauro Carvalho Chehab        self.verbose = None
182819667bcSMauro Carvalho Chehab
183819667bcSMauro Carvalho Chehab        #
184819667bcSMauro Carvalho Chehab        # Normal variables passed from Kernel's makefile
185819667bcSMauro Carvalho Chehab        #
186819667bcSMauro Carvalho Chehab        self.kernelversion = os.environ.get("KERNELVERSION", "unknown")
187819667bcSMauro Carvalho Chehab        self.kernelrelease = os.environ.get("KERNELRELEASE", "unknown")
188819667bcSMauro Carvalho Chehab        self.pdflatex = os.environ.get("PDFLATEX", "xelatex")
1892f99b85eSMauro Carvalho Chehab
1902f99b85eSMauro Carvalho Chehab        if not interactive:
191819667bcSMauro Carvalho Chehab            self.latexopts = os.environ.get("LATEXOPTS", "-interaction=batchmode -no-shell-escape")
1922f99b85eSMauro Carvalho Chehab        else:
1932f99b85eSMauro Carvalho Chehab            self.latexopts = os.environ.get("LATEXOPTS", "")
194819667bcSMauro Carvalho Chehab
195819667bcSMauro Carvalho Chehab        if not verbose:
196819667bcSMauro Carvalho Chehab            verbose = bool(os.environ.get("KBUILD_VERBOSE", "") != "")
197819667bcSMauro Carvalho Chehab
198819667bcSMauro Carvalho Chehab        if verbose is not None:
199819667bcSMauro Carvalho Chehab            self.verbose = verbose
200819667bcSMauro Carvalho Chehab
201819667bcSMauro Carvalho Chehab        #
202819667bcSMauro Carvalho Chehab        # Source tree directory. This needs to be at os.environ, as
203819667bcSMauro Carvalho Chehab        # Sphinx extensions use it
204819667bcSMauro Carvalho Chehab        #
205819667bcSMauro Carvalho Chehab        self.srctree = os.environ.get("srctree")
206819667bcSMauro Carvalho Chehab        if not self.srctree:
207819667bcSMauro Carvalho Chehab            self.srctree = "."
208819667bcSMauro Carvalho Chehab            os.environ["srctree"] = self.srctree
209819667bcSMauro Carvalho Chehab
210819667bcSMauro Carvalho Chehab        #
211819667bcSMauro Carvalho Chehab        # Now that we can expand srctree, get other directories as well
212819667bcSMauro Carvalho Chehab        #
213819667bcSMauro Carvalho Chehab        self.sphinxbuild = os.environ.get("SPHINXBUILD", "sphinx-build")
214819667bcSMauro Carvalho Chehab        self.kerneldoc = self.get_path(os.environ.get("KERNELDOC",
215819667bcSMauro Carvalho Chehab                                                      "scripts/kernel-doc.py"))
216819667bcSMauro Carvalho Chehab        self.builddir = self.get_path(builddir, use_cwd=True, abs_path=True)
217819667bcSMauro Carvalho Chehab
218819667bcSMauro Carvalho Chehab        self.config_rust = self.is_rust_enabled()
219819667bcSMauro Carvalho Chehab
220819667bcSMauro Carvalho Chehab        #
221819667bcSMauro Carvalho Chehab        # Get directory locations for LaTeX build toolchain
222819667bcSMauro Carvalho Chehab        #
223819667bcSMauro Carvalho Chehab        self.pdflatex_cmd = shutil.which(self.pdflatex)
224819667bcSMauro Carvalho Chehab        self.latexmk_cmd = shutil.which("latexmk")
225819667bcSMauro Carvalho Chehab
226819667bcSMauro Carvalho Chehab        self.env = os.environ.copy()
227819667bcSMauro Carvalho Chehab
228819667bcSMauro Carvalho Chehab        self.get_sphinx_extra_opts(n_jobs)
229819667bcSMauro Carvalho Chehab
230819667bcSMauro Carvalho Chehab    def run_sphinx(self, sphinx_build, build_args, *args, **pwargs):
231819667bcSMauro Carvalho Chehab        """
232819667bcSMauro Carvalho Chehab        Executes sphinx-build using current python3 command.
233819667bcSMauro Carvalho Chehab
234819667bcSMauro Carvalho Chehab        When calling via GNU make, POSIX jobserver is used to tell how
235819667bcSMauro Carvalho Chehab        many jobs are still available from a job pool. claim all remaining
236819667bcSMauro Carvalho Chehab        jobs, as we don't want sphinx-build to run in parallel with other
237819667bcSMauro Carvalho Chehab        jobs.
238819667bcSMauro Carvalho Chehab
239819667bcSMauro Carvalho Chehab        Despite that, the user may actually force a different value than
240819667bcSMauro Carvalho Chehab        the number of available jobs via command line.
241819667bcSMauro Carvalho Chehab
242819667bcSMauro Carvalho Chehab        The "with" logic here is used to ensure that the claimed jobs will
243819667bcSMauro Carvalho Chehab        be freed once subprocess finishes
244819667bcSMauro Carvalho Chehab        """
245819667bcSMauro Carvalho Chehab
246819667bcSMauro Carvalho Chehab        with JobserverExec() as jobserver:
247819667bcSMauro Carvalho Chehab            if jobserver.claim:
248819667bcSMauro Carvalho Chehab                #
249819667bcSMauro Carvalho Chehab                # when GNU make is used, claim available jobs from jobserver
250819667bcSMauro Carvalho Chehab                #
251819667bcSMauro Carvalho Chehab                n_jobs = str(jobserver.claim)
252819667bcSMauro Carvalho Chehab            else:
253819667bcSMauro Carvalho Chehab                #
254819667bcSMauro Carvalho Chehab                # Otherwise, let sphinx decide by default
255819667bcSMauro Carvalho Chehab                #
256819667bcSMauro Carvalho Chehab                n_jobs = "auto"
257819667bcSMauro Carvalho Chehab
258819667bcSMauro Carvalho Chehab            #
259819667bcSMauro Carvalho Chehab            # If explicitly requested via command line, override default
260819667bcSMauro Carvalho Chehab            #
261819667bcSMauro Carvalho Chehab            if self.n_jobs:
262819667bcSMauro Carvalho Chehab                n_jobs = str(self.n_jobs)
263819667bcSMauro Carvalho Chehab
264819667bcSMauro Carvalho Chehab            cmd = [sys.executable, sphinx_build]
265819667bcSMauro Carvalho Chehab            cmd += [f"-j{n_jobs}"]
266819667bcSMauro Carvalho Chehab            cmd += self.sphinxopts
267819667bcSMauro Carvalho Chehab            cmd += build_args
268819667bcSMauro Carvalho Chehab
269819667bcSMauro Carvalho Chehab            if self.verbose:
270819667bcSMauro Carvalho Chehab                print(" ".join(cmd))
271819667bcSMauro Carvalho Chehab
272819667bcSMauro Carvalho Chehab            return subprocess.call(cmd, *args, **pwargs)
273819667bcSMauro Carvalho Chehab
274819667bcSMauro Carvalho Chehab    def handle_html(self, css, output_dir):
275819667bcSMauro Carvalho Chehab        """
276819667bcSMauro Carvalho Chehab        Extra steps for HTML and epub output.
277819667bcSMauro Carvalho Chehab
278819667bcSMauro Carvalho Chehab        For such targets, we need to ensure that CSS will be properly
279819667bcSMauro Carvalho Chehab        copied to the output _static directory
280819667bcSMauro Carvalho Chehab        """
281819667bcSMauro Carvalho Chehab
282819667bcSMauro Carvalho Chehab        if not css:
283819667bcSMauro Carvalho Chehab            return
284819667bcSMauro Carvalho Chehab
285819667bcSMauro Carvalho Chehab        css = os.path.expanduser(css)
286819667bcSMauro Carvalho Chehab        if not css.startswith("/"):
287819667bcSMauro Carvalho Chehab            css = os.path.join(self.srctree, css)
288819667bcSMauro Carvalho Chehab
289819667bcSMauro Carvalho Chehab        static_dir = os.path.join(output_dir, "_static")
290819667bcSMauro Carvalho Chehab        os.makedirs(static_dir, exist_ok=True)
291819667bcSMauro Carvalho Chehab
292819667bcSMauro Carvalho Chehab        try:
293819667bcSMauro Carvalho Chehab            shutil.copy2(css, static_dir)
294819667bcSMauro Carvalho Chehab        except (OSError, IOError) as e:
295819667bcSMauro Carvalho Chehab            print(f"Warning: Failed to copy CSS: {e}", file=sys.stderr)
296819667bcSMauro Carvalho Chehab
29708e14bc1SMauro Carvalho Chehab    def build_pdf_file(self, latex_cmd, from_dir, path):
29808e14bc1SMauro Carvalho Chehab        """Builds a single pdf file using latex_cmd"""
29908e14bc1SMauro Carvalho Chehab        try:
30008e14bc1SMauro Carvalho Chehab            subprocess.run(latex_cmd + [path],
30108e14bc1SMauro Carvalho Chehab                            cwd=from_dir, check=True, env=self.env)
30208e14bc1SMauro Carvalho Chehab
30308e14bc1SMauro Carvalho Chehab            return True
30408e14bc1SMauro Carvalho Chehab        except subprocess.CalledProcessError:
30508e14bc1SMauro Carvalho Chehab            return False
30608e14bc1SMauro Carvalho Chehab
30708e14bc1SMauro Carvalho Chehab    def pdf_parallel_build(self, tex_suffix, latex_cmd, tex_files, n_jobs):
30808e14bc1SMauro Carvalho Chehab        """Build PDF files in parallel if possible"""
30908e14bc1SMauro Carvalho Chehab        builds = {}
31008e14bc1SMauro Carvalho Chehab        build_failed = False
31108e14bc1SMauro Carvalho Chehab        max_len = 0
31208e14bc1SMauro Carvalho Chehab        has_tex = False
31308e14bc1SMauro Carvalho Chehab
31408e14bc1SMauro Carvalho Chehab        #
31508e14bc1SMauro Carvalho Chehab        # LaTeX PDF error code is almost useless for us:
31608e14bc1SMauro Carvalho Chehab        # any warning makes it non-zero. For kernel doc builds it always return
31708e14bc1SMauro Carvalho Chehab        # non-zero even when build succeeds. So, let's do the best next thing:
31808e14bc1SMauro Carvalho Chehab        # Ignore build errors. At the end, check if all PDF files were built,
31908e14bc1SMauro Carvalho Chehab        # printing a summary with the built ones and returning 0 if all of
32008e14bc1SMauro Carvalho Chehab        # them were actually built.
32108e14bc1SMauro Carvalho Chehab        #
32208e14bc1SMauro Carvalho Chehab        with futures.ThreadPoolExecutor(max_workers=n_jobs) as executor:
32308e14bc1SMauro Carvalho Chehab            jobs = {}
32408e14bc1SMauro Carvalho Chehab
32508e14bc1SMauro Carvalho Chehab            for from_dir, pdf_dir, entry in tex_files:
32608e14bc1SMauro Carvalho Chehab                name = entry.name
32708e14bc1SMauro Carvalho Chehab
32808e14bc1SMauro Carvalho Chehab                if not name.endswith(tex_suffix):
32908e14bc1SMauro Carvalho Chehab                    continue
33008e14bc1SMauro Carvalho Chehab
33108e14bc1SMauro Carvalho Chehab                name = name[:-len(tex_suffix)]
33208e14bc1SMauro Carvalho Chehab                has_tex = True
33308e14bc1SMauro Carvalho Chehab
33408e14bc1SMauro Carvalho Chehab                future = executor.submit(self.build_pdf_file, latex_cmd,
33508e14bc1SMauro Carvalho Chehab                                         from_dir, entry.path)
33608e14bc1SMauro Carvalho Chehab                jobs[future] = (from_dir, pdf_dir, name)
33708e14bc1SMauro Carvalho Chehab
33808e14bc1SMauro Carvalho Chehab            for future in futures.as_completed(jobs):
33908e14bc1SMauro Carvalho Chehab                from_dir, pdf_dir, name = jobs[future]
34008e14bc1SMauro Carvalho Chehab
34108e14bc1SMauro Carvalho Chehab                pdf_name = name + ".pdf"
34208e14bc1SMauro Carvalho Chehab                pdf_from = os.path.join(from_dir, pdf_name)
343*0d9abc76SMauro Carvalho Chehab                pdf_to = os.path.join(pdf_dir, pdf_name)
344*0d9abc76SMauro Carvalho Chehab                out_name = os.path.relpath(pdf_to, self.builddir)
345*0d9abc76SMauro Carvalho Chehab                max_len = max(max_len, len(out_name))
34608e14bc1SMauro Carvalho Chehab
34708e14bc1SMauro Carvalho Chehab                try:
34808e14bc1SMauro Carvalho Chehab                    success = future.result()
34908e14bc1SMauro Carvalho Chehab
35008e14bc1SMauro Carvalho Chehab                    if success and os.path.exists(pdf_from):
35108e14bc1SMauro Carvalho Chehab                        os.rename(pdf_from, pdf_to)
35208e14bc1SMauro Carvalho Chehab
35308e14bc1SMauro Carvalho Chehab                        #
35408e14bc1SMauro Carvalho Chehab                        # if verbose, get the name of built PDF file
35508e14bc1SMauro Carvalho Chehab                        #
35608e14bc1SMauro Carvalho Chehab                        if self.verbose:
357*0d9abc76SMauro Carvalho Chehab                           builds[out_name] = "SUCCESS"
35808e14bc1SMauro Carvalho Chehab                    else:
359*0d9abc76SMauro Carvalho Chehab                        builds[out_name] = "FAILED"
36008e14bc1SMauro Carvalho Chehab                        build_failed = True
36108e14bc1SMauro Carvalho Chehab                except futures.Error as e:
362*0d9abc76SMauro Carvalho Chehab                    builds[out_name] = f"FAILED ({repr(e)})"
36308e14bc1SMauro Carvalho Chehab                    build_failed = True
36408e14bc1SMauro Carvalho Chehab
36508e14bc1SMauro Carvalho Chehab        #
36608e14bc1SMauro Carvalho Chehab        # Handle case where no .tex files were found
36708e14bc1SMauro Carvalho Chehab        #
36808e14bc1SMauro Carvalho Chehab        if not has_tex:
369*0d9abc76SMauro Carvalho Chehab            out_name = "LaTeX files"
370*0d9abc76SMauro Carvalho Chehab            max_len = max(max_len, len(out_name))
371*0d9abc76SMauro Carvalho Chehab            builds[out_name] = "FAILED: no .tex files were generated"
37208e14bc1SMauro Carvalho Chehab            build_failed = True
37308e14bc1SMauro Carvalho Chehab
37408e14bc1SMauro Carvalho Chehab        return builds, build_failed, max_len
37508e14bc1SMauro Carvalho Chehab
376819667bcSMauro Carvalho Chehab    def handle_pdf(self, output_dirs, deny_vf):
377819667bcSMauro Carvalho Chehab        """
378819667bcSMauro Carvalho Chehab        Extra steps for PDF output.
379819667bcSMauro Carvalho Chehab
380819667bcSMauro Carvalho Chehab        As PDF is handled via a LaTeX output, after building the .tex file,
381819667bcSMauro Carvalho Chehab        a new build is needed to create the PDF output from the latex
382819667bcSMauro Carvalho Chehab        directory.
383819667bcSMauro Carvalho Chehab        """
384819667bcSMauro Carvalho Chehab        builds = {}
385819667bcSMauro Carvalho Chehab        max_len = 0
38608e14bc1SMauro Carvalho Chehab        tex_suffix = ".tex"
38708e14bc1SMauro Carvalho Chehab        tex_files = []
388819667bcSMauro Carvalho Chehab
389819667bcSMauro Carvalho Chehab        #
390819667bcSMauro Carvalho Chehab        # Since early 2024, Fedora and openSUSE tumbleweed have started
391819667bcSMauro Carvalho Chehab        # deploying variable-font format of "Noto CJK", causing LaTeX
392819667bcSMauro Carvalho Chehab        # to break with CJK. Work around it, by denying the variable font
393819667bcSMauro Carvalho Chehab        # usage during xelatex build by passing the location of a config
394819667bcSMauro Carvalho Chehab        # file with a deny list.
395819667bcSMauro Carvalho Chehab        #
396819667bcSMauro Carvalho Chehab        # See tools/docs/lib/latex_fonts.py for more details.
397819667bcSMauro Carvalho Chehab        #
398819667bcSMauro Carvalho Chehab        if deny_vf:
399819667bcSMauro Carvalho Chehab            deny_vf = os.path.expanduser(deny_vf)
400819667bcSMauro Carvalho Chehab            if os.path.isdir(deny_vf):
401819667bcSMauro Carvalho Chehab                self.env["XDG_CONFIG_HOME"] = deny_vf
402819667bcSMauro Carvalho Chehab
403819667bcSMauro Carvalho Chehab        for from_dir in output_dirs:
404819667bcSMauro Carvalho Chehab            pdf_dir = os.path.join(from_dir, "../pdf")
405819667bcSMauro Carvalho Chehab            os.makedirs(pdf_dir, exist_ok=True)
406819667bcSMauro Carvalho Chehab
407819667bcSMauro Carvalho Chehab            if self.latexmk_cmd:
408819667bcSMauro Carvalho Chehab                latex_cmd = [self.latexmk_cmd, f"-{self.pdflatex}"]
409819667bcSMauro Carvalho Chehab            else:
410819667bcSMauro Carvalho Chehab                latex_cmd = [self.pdflatex]
411819667bcSMauro Carvalho Chehab
412819667bcSMauro Carvalho Chehab            latex_cmd.extend(shlex.split(self.latexopts))
413819667bcSMauro Carvalho Chehab
41408e14bc1SMauro Carvalho Chehab            # Get a list of tex files to process
415819667bcSMauro Carvalho Chehab            with os.scandir(from_dir) as it:
416819667bcSMauro Carvalho Chehab                for entry in it:
41708e14bc1SMauro Carvalho Chehab                    if entry.name.endswith(tex_suffix):
41808e14bc1SMauro Carvalho Chehab                        tex_files.append((from_dir, pdf_dir, entry))
419819667bcSMauro Carvalho Chehab
420819667bcSMauro Carvalho Chehab        #
42108e14bc1SMauro Carvalho Chehab        # When using make, this won't be used, as the number of jobs comes
42208e14bc1SMauro Carvalho Chehab        # from POSIX jobserver. So, this covers the case where build comes
42308e14bc1SMauro Carvalho Chehab        # from command line. On such case, serialize by default, except if
42408e14bc1SMauro Carvalho Chehab        # the user explicitly sets the number of jobs.
425819667bcSMauro Carvalho Chehab        #
42608e14bc1SMauro Carvalho Chehab        n_jobs = 1
42708e14bc1SMauro Carvalho Chehab
42808e14bc1SMauro Carvalho Chehab        # n_jobs is either an integer or "auto". Only use it if it is a number
42908e14bc1SMauro Carvalho Chehab        if self.n_jobs:
430819667bcSMauro Carvalho Chehab            try:
43108e14bc1SMauro Carvalho Chehab                n_jobs = int(self.n_jobs)
43208e14bc1SMauro Carvalho Chehab            except ValueError:
433819667bcSMauro Carvalho Chehab                pass
434819667bcSMauro Carvalho Chehab
43508e14bc1SMauro Carvalho Chehab        #
43608e14bc1SMauro Carvalho Chehab        # When using make, jobserver.claim is the number of jobs that were
43708e14bc1SMauro Carvalho Chehab        # used with "-j" and that aren't used by other make targets
43808e14bc1SMauro Carvalho Chehab        #
43908e14bc1SMauro Carvalho Chehab        with JobserverExec() as jobserver:
44008e14bc1SMauro Carvalho Chehab            n_jobs = 1
441819667bcSMauro Carvalho Chehab
44208e14bc1SMauro Carvalho Chehab            #
44308e14bc1SMauro Carvalho Chehab            # Handle the case when a parameter is passed via command line,
44408e14bc1SMauro Carvalho Chehab            # using it as default, if jobserver doesn't claim anything
44508e14bc1SMauro Carvalho Chehab            #
44608e14bc1SMauro Carvalho Chehab            if self.n_jobs:
44708e14bc1SMauro Carvalho Chehab                try:
44808e14bc1SMauro Carvalho Chehab                    n_jobs = int(self.n_jobs)
44908e14bc1SMauro Carvalho Chehab                except ValueError:
45008e14bc1SMauro Carvalho Chehab                    pass
451819667bcSMauro Carvalho Chehab
45208e14bc1SMauro Carvalho Chehab            if jobserver.claim:
45308e14bc1SMauro Carvalho Chehab                n_jobs = jobserver.claim
454819667bcSMauro Carvalho Chehab
45508e14bc1SMauro Carvalho Chehab            builds, build_failed, max_len = self.pdf_parallel_build(tex_suffix,
45608e14bc1SMauro Carvalho Chehab                                                                    latex_cmd,
45708e14bc1SMauro Carvalho Chehab                                                                    tex_files,
45808e14bc1SMauro Carvalho Chehab                                                                    n_jobs)
459819667bcSMauro Carvalho Chehab
46008e14bc1SMauro Carvalho Chehab        #
46108e14bc1SMauro Carvalho Chehab        # In verbose mode, print a summary with the build results per file.
46208e14bc1SMauro Carvalho Chehab        # Otherwise, print a single line with all failures, if any.
46308e14bc1SMauro Carvalho Chehab        # On both cases, return code 1 indicates build failures,
46408e14bc1SMauro Carvalho Chehab        #
46508e14bc1SMauro Carvalho Chehab        if self.verbose:
466819667bcSMauro Carvalho Chehab            msg = "Summary"
467819667bcSMauro Carvalho Chehab            msg += "\n" + "=" * len(msg)
468819667bcSMauro Carvalho Chehab            print()
469819667bcSMauro Carvalho Chehab            print(msg)
470819667bcSMauro Carvalho Chehab
471819667bcSMauro Carvalho Chehab            for pdf_name, pdf_file in builds.items():
472819667bcSMauro Carvalho Chehab                print(f"{pdf_name:<{max_len}}: {pdf_file}")
473819667bcSMauro Carvalho Chehab
474819667bcSMauro Carvalho Chehab            print()
475819667bcSMauro Carvalho Chehab            if build_failed:
476819667bcSMauro Carvalho Chehab                msg = LatexFontChecker().check()
477819667bcSMauro Carvalho Chehab                if msg:
478819667bcSMauro Carvalho Chehab                    print(msg)
479819667bcSMauro Carvalho Chehab
48008e14bc1SMauro Carvalho Chehab                sys.exit("Error: not all PDF files were created.")
48108e14bc1SMauro Carvalho Chehab
48208e14bc1SMauro Carvalho Chehab        elif build_failed:
48308e14bc1SMauro Carvalho Chehab            n_failures = len(builds)
48408e14bc1SMauro Carvalho Chehab            failures = ", ".join(builds.keys())
48508e14bc1SMauro Carvalho Chehab
48608e14bc1SMauro Carvalho Chehab            msg = LatexFontChecker().check()
48708e14bc1SMauro Carvalho Chehab            if msg:
48808e14bc1SMauro Carvalho Chehab                print(msg)
48908e14bc1SMauro Carvalho Chehab
49008e14bc1SMauro Carvalho Chehab            sys.exit(f"Error: Can't build {n_failures} PDF file(s): {failures}")
491819667bcSMauro Carvalho Chehab
492819667bcSMauro Carvalho Chehab    def handle_info(self, output_dirs):
493819667bcSMauro Carvalho Chehab        """
494819667bcSMauro Carvalho Chehab        Extra steps for Info output.
495819667bcSMauro Carvalho Chehab
496819667bcSMauro Carvalho Chehab        For texinfo generation, an additional make is needed from the
497819667bcSMauro Carvalho Chehab        texinfo directory.
498819667bcSMauro Carvalho Chehab        """
499819667bcSMauro Carvalho Chehab
500819667bcSMauro Carvalho Chehab        for output_dir in output_dirs:
501819667bcSMauro Carvalho Chehab            try:
502819667bcSMauro Carvalho Chehab                subprocess.run(["make", "info"], cwd=output_dir, check=True)
503819667bcSMauro Carvalho Chehab            except subprocess.CalledProcessError as e:
504819667bcSMauro Carvalho Chehab                sys.exit(f"Error generating info docs: {e}")
505819667bcSMauro Carvalho Chehab
506819667bcSMauro Carvalho Chehab    def cleandocs(self, builder):           # pylint: disable=W0613
507819667bcSMauro Carvalho Chehab        """Remove documentation output directory"""
508819667bcSMauro Carvalho Chehab        shutil.rmtree(self.builddir, ignore_errors=True)
509819667bcSMauro Carvalho Chehab
510819667bcSMauro Carvalho Chehab    def build(self, target, sphinxdirs=None, conf="conf.py",
511819667bcSMauro Carvalho Chehab              theme=None, css=None, paper=None, deny_vf=None):
512819667bcSMauro Carvalho Chehab        """
513819667bcSMauro Carvalho Chehab        Build documentation using Sphinx. This is the core function of this
514819667bcSMauro Carvalho Chehab        module. It prepares all arguments required by sphinx-build.
515819667bcSMauro Carvalho Chehab        """
516819667bcSMauro Carvalho Chehab
517819667bcSMauro Carvalho Chehab        builder = TARGETS[target]["builder"]
518819667bcSMauro Carvalho Chehab        out_dir = TARGETS[target].get("out_dir", "")
519819667bcSMauro Carvalho Chehab
520819667bcSMauro Carvalho Chehab        #
521819667bcSMauro Carvalho Chehab        # Cleandocs doesn't require sphinx-build
522819667bcSMauro Carvalho Chehab        #
523819667bcSMauro Carvalho Chehab        if target == "cleandocs":
524819667bcSMauro Carvalho Chehab            self.cleandocs(builder)
525819667bcSMauro Carvalho Chehab            return
526819667bcSMauro Carvalho Chehab
527819667bcSMauro Carvalho Chehab        if theme:
528819667bcSMauro Carvalho Chehab            os.environ["DOCS_THEME"] = theme
529819667bcSMauro Carvalho Chehab
530819667bcSMauro Carvalho Chehab        #
531819667bcSMauro Carvalho Chehab        # Other targets require sphinx-build, so check if it exists
532819667bcSMauro Carvalho Chehab        #
533819667bcSMauro Carvalho Chehab        sphinxbuild = shutil.which(self.sphinxbuild, path=self.env["PATH"])
534819667bcSMauro Carvalho Chehab        if not sphinxbuild:
535819667bcSMauro Carvalho Chehab            sys.exit(f"Error: {self.sphinxbuild} not found in PATH.\n")
536819667bcSMauro Carvalho Chehab
537819667bcSMauro Carvalho Chehab        if builder == "latex":
538819667bcSMauro Carvalho Chehab            if not self.pdflatex_cmd and not self.latexmk_cmd:
539819667bcSMauro Carvalho Chehab                sys.exit("Error: pdflatex or latexmk required for PDF generation")
540819667bcSMauro Carvalho Chehab
541819667bcSMauro Carvalho Chehab        docs_dir = os.path.abspath(os.path.join(self.srctree, "Documentation"))
542819667bcSMauro Carvalho Chehab
543819667bcSMauro Carvalho Chehab        #
544819667bcSMauro Carvalho Chehab        # Fill in base arguments for Sphinx build
545819667bcSMauro Carvalho Chehab        #
546819667bcSMauro Carvalho Chehab        kerneldoc = self.kerneldoc
547819667bcSMauro Carvalho Chehab        if kerneldoc.startswith(self.srctree):
548819667bcSMauro Carvalho Chehab            kerneldoc = os.path.relpath(kerneldoc, self.srctree)
549819667bcSMauro Carvalho Chehab
550819667bcSMauro Carvalho Chehab        args = [ "-b", builder, "-c", docs_dir ]
551819667bcSMauro Carvalho Chehab
552819667bcSMauro Carvalho Chehab        if builder == "latex":
553819667bcSMauro Carvalho Chehab            if not paper:
554819667bcSMauro Carvalho Chehab                paper = PAPER[1]
555819667bcSMauro Carvalho Chehab
556819667bcSMauro Carvalho Chehab            args.extend(["-D", f"latex_elements.papersize={paper}paper"])
557819667bcSMauro Carvalho Chehab
558819667bcSMauro Carvalho Chehab        if self.config_rust:
559819667bcSMauro Carvalho Chehab            args.extend(["-t", "rustdoc"])
560819667bcSMauro Carvalho Chehab
561819667bcSMauro Carvalho Chehab        if conf:
562819667bcSMauro Carvalho Chehab            self.env["SPHINX_CONF"] = self.get_path(conf, abs_path=True)
563819667bcSMauro Carvalho Chehab
564819667bcSMauro Carvalho Chehab        if not sphinxdirs:
565819667bcSMauro Carvalho Chehab            sphinxdirs = os.environ.get("SPHINXDIRS", ".")
566819667bcSMauro Carvalho Chehab
567819667bcSMauro Carvalho Chehab        #
56882c294d4SMauro Carvalho Chehab        # The sphinx-build tool has a bug: internally, it tries to set
56982c294d4SMauro Carvalho Chehab        # locale with locale.setlocale(locale.LC_ALL, ''). This causes a
57082c294d4SMauro Carvalho Chehab        # crash if language is not set. Detect and fix it.
57182c294d4SMauro Carvalho Chehab        #
57282c294d4SMauro Carvalho Chehab        try:
57382c294d4SMauro Carvalho Chehab            locale.setlocale(locale.LC_ALL, '')
57482c294d4SMauro Carvalho Chehab        except locale.Error:
57582c294d4SMauro Carvalho Chehab            self.env["LC_ALL"] = "C"
57682c294d4SMauro Carvalho Chehab
57782c294d4SMauro Carvalho Chehab        #
578819667bcSMauro Carvalho Chehab        # sphinxdirs can be a list or a whitespace-separated string
579819667bcSMauro Carvalho Chehab        #
580819667bcSMauro Carvalho Chehab        sphinxdirs_list = []
581819667bcSMauro Carvalho Chehab        for sphinxdir in sphinxdirs:
582819667bcSMauro Carvalho Chehab            if isinstance(sphinxdir, list):
583819667bcSMauro Carvalho Chehab                sphinxdirs_list += sphinxdir
584819667bcSMauro Carvalho Chehab            else:
585819667bcSMauro Carvalho Chehab                sphinxdirs_list += sphinxdir.split()
586819667bcSMauro Carvalho Chehab
587819667bcSMauro Carvalho Chehab        #
588819667bcSMauro Carvalho Chehab        # Step 1:  Build each directory in separate.
589819667bcSMauro Carvalho Chehab        #
590819667bcSMauro Carvalho Chehab        # This is not the best way of handling it, as cross-references between
591819667bcSMauro Carvalho Chehab        # them will be broken, but this is what we've been doing since
592819667bcSMauro Carvalho Chehab        # the beginning.
593819667bcSMauro Carvalho Chehab        #
594819667bcSMauro Carvalho Chehab        output_dirs = []
595819667bcSMauro Carvalho Chehab        for sphinxdir in sphinxdirs_list:
596819667bcSMauro Carvalho Chehab            src_dir = os.path.join(docs_dir, sphinxdir)
597819667bcSMauro Carvalho Chehab            doctree_dir = os.path.join(self.builddir, ".doctrees")
598819667bcSMauro Carvalho Chehab            output_dir = os.path.join(self.builddir, sphinxdir, out_dir)
599819667bcSMauro Carvalho Chehab
600819667bcSMauro Carvalho Chehab            #
601819667bcSMauro Carvalho Chehab            # Make directory names canonical
602819667bcSMauro Carvalho Chehab            #
603819667bcSMauro Carvalho Chehab            src_dir = os.path.normpath(src_dir)
604819667bcSMauro Carvalho Chehab            doctree_dir = os.path.normpath(doctree_dir)
605819667bcSMauro Carvalho Chehab            output_dir = os.path.normpath(output_dir)
606819667bcSMauro Carvalho Chehab
607819667bcSMauro Carvalho Chehab            os.makedirs(doctree_dir, exist_ok=True)
608819667bcSMauro Carvalho Chehab            os.makedirs(output_dir, exist_ok=True)
609819667bcSMauro Carvalho Chehab
610819667bcSMauro Carvalho Chehab            output_dirs.append(output_dir)
611819667bcSMauro Carvalho Chehab
612819667bcSMauro Carvalho Chehab            build_args = args + [
613819667bcSMauro Carvalho Chehab                "-d", doctree_dir,
614819667bcSMauro Carvalho Chehab                "-D", f"kerneldoc_bin={kerneldoc}",
615819667bcSMauro Carvalho Chehab                "-D", f"version={self.kernelversion}",
616819667bcSMauro Carvalho Chehab                "-D", f"release={self.kernelrelease}",
617819667bcSMauro Carvalho Chehab                "-D", f"kerneldoc_srctree={self.srctree}",
618819667bcSMauro Carvalho Chehab                src_dir,
619819667bcSMauro Carvalho Chehab                output_dir,
620819667bcSMauro Carvalho Chehab            ]
621819667bcSMauro Carvalho Chehab
622819667bcSMauro Carvalho Chehab            try:
623819667bcSMauro Carvalho Chehab                self.run_sphinx(sphinxbuild, build_args, env=self.env)
624819667bcSMauro Carvalho Chehab            except (OSError, ValueError, subprocess.SubprocessError) as e:
625819667bcSMauro Carvalho Chehab                sys.exit(f"Build failed: {repr(e)}")
626819667bcSMauro Carvalho Chehab
627819667bcSMauro Carvalho Chehab            #
628819667bcSMauro Carvalho Chehab            # Ensure that each html/epub output will have needed static files
629819667bcSMauro Carvalho Chehab            #
630819667bcSMauro Carvalho Chehab            if target in ["htmldocs", "epubdocs"]:
631819667bcSMauro Carvalho Chehab                self.handle_html(css, output_dir)
632819667bcSMauro Carvalho Chehab
633819667bcSMauro Carvalho Chehab        #
634819667bcSMauro Carvalho Chehab        # Step 2: Some targets (PDF and info) require an extra step once
635819667bcSMauro Carvalho Chehab        #         sphinx-build finishes
636819667bcSMauro Carvalho Chehab        #
637819667bcSMauro Carvalho Chehab        if target == "pdfdocs":
638819667bcSMauro Carvalho Chehab            self.handle_pdf(output_dirs, deny_vf)
639819667bcSMauro Carvalho Chehab        elif target == "infodocs":
640819667bcSMauro Carvalho Chehab            self.handle_info(output_dirs)
641819667bcSMauro Carvalho Chehab
642819667bcSMauro Carvalho Chehabdef jobs_type(value):
643819667bcSMauro Carvalho Chehab    """
644819667bcSMauro Carvalho Chehab    Handle valid values for -j. Accepts Sphinx "-jauto", plus a number
645819667bcSMauro Carvalho Chehab    equal or bigger than one.
646819667bcSMauro Carvalho Chehab    """
647819667bcSMauro Carvalho Chehab    if value is None:
648819667bcSMauro Carvalho Chehab        return None
649819667bcSMauro Carvalho Chehab
650819667bcSMauro Carvalho Chehab    if value.lower() == 'auto':
651819667bcSMauro Carvalho Chehab        return value.lower()
652819667bcSMauro Carvalho Chehab
653819667bcSMauro Carvalho Chehab    try:
654819667bcSMauro Carvalho Chehab        if int(value) >= 1:
655819667bcSMauro Carvalho Chehab            return value
656819667bcSMauro Carvalho Chehab
657819667bcSMauro Carvalho Chehab        raise argparse.ArgumentTypeError(f"Minimum jobs is 1, got {value}")
658819667bcSMauro Carvalho Chehab    except ValueError:
659819667bcSMauro Carvalho Chehab        raise argparse.ArgumentTypeError(f"Must be 'auto' or positive integer, got {value}")  # pylint: disable=W0707
660819667bcSMauro Carvalho Chehab
661819667bcSMauro Carvalho Chehabdef main():
662819667bcSMauro Carvalho Chehab    """
663819667bcSMauro Carvalho Chehab    Main function. The only mandatory argument is the target. If not
664819667bcSMauro Carvalho Chehab    specified, the other arguments will use default values if not
665819667bcSMauro Carvalho Chehab    specified at os.environ.
666819667bcSMauro Carvalho Chehab    """
667819667bcSMauro Carvalho Chehab    parser = argparse.ArgumentParser(description="Kernel documentation builder")
668819667bcSMauro Carvalho Chehab
669819667bcSMauro Carvalho Chehab    parser.add_argument("target", choices=list(TARGETS.keys()),
670819667bcSMauro Carvalho Chehab                        help="Documentation target to build")
671819667bcSMauro Carvalho Chehab    parser.add_argument("--sphinxdirs", nargs="+",
672819667bcSMauro Carvalho Chehab                        help="Specific directories to build")
673819667bcSMauro Carvalho Chehab    parser.add_argument("--conf", default="conf.py",
674819667bcSMauro Carvalho Chehab                        help="Sphinx configuration file")
675819667bcSMauro Carvalho Chehab    parser.add_argument("--builddir", default="output",
676819667bcSMauro Carvalho Chehab                        help="Sphinx configuration file")
677819667bcSMauro Carvalho Chehab
678819667bcSMauro Carvalho Chehab    parser.add_argument("--theme", help="Sphinx theme to use")
679819667bcSMauro Carvalho Chehab
680819667bcSMauro Carvalho Chehab    parser.add_argument("--css", help="Custom CSS file for HTML/EPUB")
681819667bcSMauro Carvalho Chehab
682819667bcSMauro Carvalho Chehab    parser.add_argument("--paper", choices=PAPER, default=PAPER[0],
683819667bcSMauro Carvalho Chehab                        help="Paper size for LaTeX/PDF output")
684819667bcSMauro Carvalho Chehab
685819667bcSMauro Carvalho Chehab    parser.add_argument('--deny-vf',
686819667bcSMauro Carvalho Chehab                        help="Configuration to deny variable fonts on pdf builds")
687819667bcSMauro Carvalho Chehab
688819667bcSMauro Carvalho Chehab    parser.add_argument("-v", "--verbose", action='store_true',
689819667bcSMauro Carvalho Chehab                        help="place build in verbose mode")
690819667bcSMauro Carvalho Chehab
691819667bcSMauro Carvalho Chehab    parser.add_argument('-j', '--jobs', type=jobs_type,
692819667bcSMauro Carvalho Chehab                        help="Sets number of jobs to use with sphinx-build")
693819667bcSMauro Carvalho Chehab
6942f99b85eSMauro Carvalho Chehab    parser.add_argument('-i', '--interactive', action='store_true',
6952f99b85eSMauro Carvalho Chehab                        help="Change latex default to run in interactive mode")
6962f99b85eSMauro Carvalho Chehab
697819667bcSMauro Carvalho Chehab    args = parser.parse_args()
698819667bcSMauro Carvalho Chehab
699819667bcSMauro Carvalho Chehab    PythonVersion.check_python(MIN_PYTHON_VERSION)
700819667bcSMauro Carvalho Chehab
701819667bcSMauro Carvalho Chehab    builder = SphinxBuilder(builddir=args.builddir,
7022f99b85eSMauro Carvalho Chehab                            verbose=args.verbose, n_jobs=args.jobs,
7032f99b85eSMauro Carvalho Chehab                            interactive=args.interactive)
704819667bcSMauro Carvalho Chehab
705819667bcSMauro Carvalho Chehab    builder.build(args.target, sphinxdirs=args.sphinxdirs, conf=args.conf,
706819667bcSMauro Carvalho Chehab                  theme=args.theme, css=args.css, paper=args.paper,
707819667bcSMauro Carvalho Chehab                  deny_vf=args.deny_vf)
708819667bcSMauro Carvalho Chehab
709819667bcSMauro Carvalho Chehabif __name__ == "__main__":
710819667bcSMauro Carvalho Chehab    main()
711