154c147f4SMauro Carvalho Chehab#!/usr/bin/env python3 254c147f4SMauro Carvalho Chehab# SPDX-License-Identifier: GPL-2.0 354c147f4SMauro Carvalho Chehab# Copyright(c) 2025: Mauro Carvalho Chehab <mchehab+huawei@kernel.org> 454c147f4SMauro Carvalho Chehab# 5fb1e8d12SMauro Carvalho Chehab# pylint: disable=R0903,R0912,R0913,R0914,R0917,C0301 654c147f4SMauro Carvalho Chehab 754c147f4SMauro Carvalho Chehab""" 854c147f4SMauro Carvalho ChehabInstall minimal supported requirements for different Sphinx versions 954c147f4SMauro Carvalho Chehaband optionally test the build. 1054c147f4SMauro Carvalho Chehab""" 1154c147f4SMauro Carvalho Chehab 1254c147f4SMauro Carvalho Chehabimport argparse 137649db7dSMauro Carvalho Chehabimport asyncio 1454c147f4SMauro Carvalho Chehabimport os.path 153fa60d28SMauro Carvalho Chehabimport shutil 1654c147f4SMauro Carvalho Chehabimport sys 1754c147f4SMauro Carvalho Chehabimport time 187649db7dSMauro Carvalho Chehabimport subprocess 1954c147f4SMauro Carvalho Chehab 203fa60d28SMauro Carvalho Chehab# Minimal python version supported by the building system. 2154c147f4SMauro Carvalho Chehab 223fa60d28SMauro Carvalho ChehabPYTHON = os.path.basename(sys.executable) 233fa60d28SMauro Carvalho Chehab 243fa60d28SMauro Carvalho Chehabmin_python_bin = None 253fa60d28SMauro Carvalho Chehab 263fa60d28SMauro Carvalho Chehabfor i in range(9, 13): 273fa60d28SMauro Carvalho Chehab p = f"python3.{i}" 283fa60d28SMauro Carvalho Chehab if shutil.which(p): 293fa60d28SMauro Carvalho Chehab min_python_bin = p 303fa60d28SMauro Carvalho Chehab break 313fa60d28SMauro Carvalho Chehab 323fa60d28SMauro Carvalho Chehabif not min_python_bin: 333fa60d28SMauro Carvalho Chehab min_python_bin = PYTHON 343fa60d28SMauro Carvalho Chehab 353fa60d28SMauro Carvalho Chehab# Starting from 8.0, Python 3.9 is not supported anymore. 36791b9b03SMauro Carvalho ChehabPYTHON_VER_CHANGES = {(8, 0, 0): PYTHON} 37791b9b03SMauro Carvalho Chehab 38791b9b03SMauro Carvalho ChehabDEFAULT_VERSIONS_TO_TEST = [ 39791b9b03SMauro Carvalho Chehab (3, 4, 3), # Minimal supported version 40791b9b03SMauro Carvalho Chehab (5, 3, 0), # CentOS Stream 9 / AlmaLinux 9 41791b9b03SMauro Carvalho Chehab (6, 1, 1), # Debian 12 42791b9b03SMauro Carvalho Chehab (7, 2, 1), # openSUSE Leap 15.6 43791b9b03SMauro Carvalho Chehab (7, 2, 6), # Ubuntu 24.04 LTS 44791b9b03SMauro Carvalho Chehab (7, 4, 7), # Ubuntu 24.10 45791b9b03SMauro Carvalho Chehab (7, 3, 0), # openSUSE Tumbleweed 46791b9b03SMauro Carvalho Chehab (8, 1, 3), # Fedora 42 47791b9b03SMauro Carvalho Chehab (8, 2, 3) # Latest version - covers rolling distros 48791b9b03SMauro Carvalho Chehab] 4954c147f4SMauro Carvalho Chehab 5054c147f4SMauro Carvalho Chehab# Sphinx versions to be installed and their incremental requirements 517649db7dSMauro Carvalho ChehabSPHINX_REQUIREMENTS = { 52792bf019SMauro Carvalho Chehab # Oldest versions we support for each package required by Sphinx 3.4.3 5354c147f4SMauro Carvalho Chehab (3, 4, 3): { 54792bf019SMauro Carvalho Chehab "docutils": "0.16", 55792bf019SMauro Carvalho Chehab "alabaster": "0.7.12", 56792bf019SMauro Carvalho Chehab "babel": "2.8.0", 57792bf019SMauro Carvalho Chehab "certifi": "2020.6.20", 58792bf019SMauro Carvalho Chehab "docutils": "0.16", 59792bf019SMauro Carvalho Chehab "idna": "2.10", 60792bf019SMauro Carvalho Chehab "imagesize": "1.2.0", 61792bf019SMauro Carvalho Chehab "Jinja2": "2.11.2", 62792bf019SMauro Carvalho Chehab "MarkupSafe": "1.1.1", 63792bf019SMauro Carvalho Chehab "packaging": "20.4", 64792bf019SMauro Carvalho Chehab "Pygments": "2.6.1", 65792bf019SMauro Carvalho Chehab "PyYAML": "5.1", 66792bf019SMauro Carvalho Chehab "requests": "2.24.0", 67792bf019SMauro Carvalho Chehab "snowballstemmer": "2.0.0", 68792bf019SMauro Carvalho Chehab "sphinxcontrib-applehelp": "1.0.2", 69792bf019SMauro Carvalho Chehab "sphinxcontrib-devhelp": "1.0.2", 70792bf019SMauro Carvalho Chehab "sphinxcontrib-htmlhelp": "1.0.3", 71792bf019SMauro Carvalho Chehab "sphinxcontrib-jsmath": "1.0.1", 72792bf019SMauro Carvalho Chehab "sphinxcontrib-qthelp": "1.0.3", 73792bf019SMauro Carvalho Chehab "sphinxcontrib-serializinghtml": "1.1.4", 74792bf019SMauro Carvalho Chehab "urllib3": "1.25.9", 75792bf019SMauro Carvalho Chehab }, 76792bf019SMauro Carvalho Chehab 77792bf019SMauro Carvalho Chehab # Update package dependencies to a more modern base. The goal here 78792bf019SMauro Carvalho Chehab # is to avoid to many incremental changes for the next entries 79791b9b03SMauro Carvalho Chehab (3, 5, 0): { 8054c147f4SMauro Carvalho Chehab "alabaster": "0.7.13", 8154c147f4SMauro Carvalho Chehab "babel": "2.17.0", 8254c147f4SMauro Carvalho Chehab "certifi": "2025.6.15", 8354c147f4SMauro Carvalho Chehab "idna": "3.10", 8454c147f4SMauro Carvalho Chehab "imagesize": "1.4.1", 8554c147f4SMauro Carvalho Chehab "packaging": "25.0", 86791b9b03SMauro Carvalho Chehab "Pygments": "2.8.1", 8754c147f4SMauro Carvalho Chehab "requests": "2.32.4", 8854c147f4SMauro Carvalho Chehab "snowballstemmer": "3.0.1", 8954c147f4SMauro Carvalho Chehab "sphinxcontrib-applehelp": "1.0.4", 9054c147f4SMauro Carvalho Chehab "sphinxcontrib-htmlhelp": "2.0.1", 9154c147f4SMauro Carvalho Chehab "sphinxcontrib-serializinghtml": "1.1.5", 92791b9b03SMauro Carvalho Chehab "urllib3": "2.0.0", 9354c147f4SMauro Carvalho Chehab }, 94792bf019SMauro Carvalho Chehab 95792bf019SMauro Carvalho Chehab # Starting from here, ensure all docutils versions are covered with 96792bf019SMauro Carvalho Chehab # supported Sphinx versions. Other packages are upgraded only when 97792bf019SMauro Carvalho Chehab # required by pip 98791b9b03SMauro Carvalho Chehab (4, 0, 0): { 9954c147f4SMauro Carvalho Chehab "PyYAML": "5.1", 10054c147f4SMauro Carvalho Chehab }, 101791b9b03SMauro Carvalho Chehab (4, 1, 0): { 102791b9b03SMauro Carvalho Chehab "docutils": "0.17", 103791b9b03SMauro Carvalho Chehab "Pygments": "2.19.1", 104791b9b03SMauro Carvalho Chehab "Jinja2": "3.0.3", 105791b9b03SMauro Carvalho Chehab "MarkupSafe": "2.0", 106792bf019SMauro Carvalho Chehab }, 107791b9b03SMauro Carvalho Chehab (4, 3, 0): {}, 10854c147f4SMauro Carvalho Chehab (4, 4, 0): {}, 109791b9b03SMauro Carvalho Chehab (4, 5, 0): { 110792bf019SMauro Carvalho Chehab "docutils": "0.17.1", 111791b9b03SMauro Carvalho Chehab }, 112791b9b03SMauro Carvalho Chehab (5, 0, 0): {}, 113791b9b03SMauro Carvalho Chehab (5, 1, 0): {}, 114791b9b03SMauro Carvalho Chehab (5, 2, 0): { 115791b9b03SMauro Carvalho Chehab "docutils": "0.18", 11654c147f4SMauro Carvalho Chehab "Jinja2": "3.1.2", 11754c147f4SMauro Carvalho Chehab "MarkupSafe": "2.0", 11854c147f4SMauro Carvalho Chehab "PyYAML": "5.3.1", 11954c147f4SMauro Carvalho Chehab }, 120791b9b03SMauro Carvalho Chehab (5, 3, 0): { 121792bf019SMauro Carvalho Chehab "docutils": "0.18.1", 122791b9b03SMauro Carvalho Chehab }, 123791b9b03SMauro Carvalho Chehab (6, 0, 0): {}, 124791b9b03SMauro Carvalho Chehab (6, 1, 0): {}, 125791b9b03SMauro Carvalho Chehab (6, 2, 0): { 12654c147f4SMauro Carvalho Chehab "PyYAML": "5.4.1", 12754c147f4SMauro Carvalho Chehab }, 128791b9b03SMauro Carvalho Chehab (7, 0, 0): {}, 129791b9b03SMauro Carvalho Chehab (7, 1, 0): {}, 130791b9b03SMauro Carvalho Chehab (7, 2, 0): { 131792bf019SMauro Carvalho Chehab "docutils": "0.19", 13254c147f4SMauro Carvalho Chehab "PyYAML": "6.0.1", 13354c147f4SMauro Carvalho Chehab "sphinxcontrib-serializinghtml": "1.1.9", 13454c147f4SMauro Carvalho Chehab }, 135791b9b03SMauro Carvalho Chehab (7, 2, 6): { 136792bf019SMauro Carvalho Chehab "docutils": "0.20", 137791b9b03SMauro Carvalho Chehab }, 138791b9b03SMauro Carvalho Chehab (7, 3, 0): { 13954c147f4SMauro Carvalho Chehab "alabaster": "0.7.14", 14054c147f4SMauro Carvalho Chehab "PyYAML": "6.0.1", 141791b9b03SMauro Carvalho Chehab "tomli": "2.0.1", 14254c147f4SMauro Carvalho Chehab }, 143791b9b03SMauro Carvalho Chehab (7, 4, 0): { 144791b9b03SMauro Carvalho Chehab "docutils": "0.20.1", 14554c147f4SMauro Carvalho Chehab "PyYAML": "6.0.1", 14654c147f4SMauro Carvalho Chehab }, 147791b9b03SMauro Carvalho Chehab (8, 0, 0): { 148791b9b03SMauro Carvalho Chehab "docutils": "0.21", 149792bf019SMauro Carvalho Chehab }, 150791b9b03SMauro Carvalho Chehab (8, 1, 0): { 151791b9b03SMauro Carvalho Chehab "docutils": "0.21.1", 15254c147f4SMauro Carvalho Chehab "PyYAML": "6.0.1", 15354c147f4SMauro Carvalho Chehab "sphinxcontrib-applehelp": "1.0.7", 15454c147f4SMauro Carvalho Chehab "sphinxcontrib-devhelp": "1.0.6", 15554c147f4SMauro Carvalho Chehab "sphinxcontrib-htmlhelp": "2.0.6", 15654c147f4SMauro Carvalho Chehab "sphinxcontrib-qthelp": "1.0.6", 15754c147f4SMauro Carvalho Chehab }, 158791b9b03SMauro Carvalho Chehab (8, 2, 0): { 159791b9b03SMauro Carvalho Chehab "docutils": "0.21.2", 16054c147f4SMauro Carvalho Chehab "PyYAML": "6.0.1", 16154c147f4SMauro Carvalho Chehab "sphinxcontrib-serializinghtml": "1.1.9", 16254c147f4SMauro Carvalho Chehab }, 16354c147f4SMauro Carvalho Chehab} 16454c147f4SMauro Carvalho Chehab 16554c147f4SMauro Carvalho Chehab 1667649db7dSMauro Carvalho Chehabclass AsyncCommands: 1677649db7dSMauro Carvalho Chehab """Excecute command synchronously""" 1687649db7dSMauro Carvalho Chehab 169fb1e8d12SMauro Carvalho Chehab def __init__(self, fp=None): 170fb1e8d12SMauro Carvalho Chehab 171fb1e8d12SMauro Carvalho Chehab self.stdout = None 172fb1e8d12SMauro Carvalho Chehab self.stderr = None 173fb1e8d12SMauro Carvalho Chehab self.output = None 174fb1e8d12SMauro Carvalho Chehab self.fp = fp 175fb1e8d12SMauro Carvalho Chehab 176fb1e8d12SMauro Carvalho Chehab def log(self, out, verbose, is_info=True): 1770e93f124SMauro Carvalho Chehab out = out.removesuffix('\n') 1780e93f124SMauro Carvalho Chehab 179fb1e8d12SMauro Carvalho Chehab if verbose: 180fb1e8d12SMauro Carvalho Chehab if is_info: 1810e93f124SMauro Carvalho Chehab print(out) 182fb1e8d12SMauro Carvalho Chehab else: 1830e93f124SMauro Carvalho Chehab print(out, file=sys.stderr) 184fb1e8d12SMauro Carvalho Chehab 185fb1e8d12SMauro Carvalho Chehab if self.fp: 1860e93f124SMauro Carvalho Chehab self.fp.write(out + "\n") 1877649db7dSMauro Carvalho Chehab 1887649db7dSMauro Carvalho Chehab async def _read(self, stream, verbose, is_info): 1897649db7dSMauro Carvalho Chehab """Ancillary routine to capture while displaying""" 1907649db7dSMauro Carvalho Chehab 1917649db7dSMauro Carvalho Chehab while stream is not None: 1927649db7dSMauro Carvalho Chehab line = await stream.readline() 1937649db7dSMauro Carvalho Chehab if line: 1947649db7dSMauro Carvalho Chehab out = line.decode("utf-8", errors="backslashreplace") 195fb1e8d12SMauro Carvalho Chehab self.log(out, verbose, is_info) 1967649db7dSMauro Carvalho Chehab if is_info: 1977649db7dSMauro Carvalho Chehab self.stdout += out 1987649db7dSMauro Carvalho Chehab else: 1997649db7dSMauro Carvalho Chehab self.stderr += out 2007649db7dSMauro Carvalho Chehab else: 2017649db7dSMauro Carvalho Chehab break 2027649db7dSMauro Carvalho Chehab 2037649db7dSMauro Carvalho Chehab async def run(self, cmd, capture_output=False, check=False, 2047649db7dSMauro Carvalho Chehab env=None, verbose=True): 2057649db7dSMauro Carvalho Chehab 2067649db7dSMauro Carvalho Chehab """ 2077649db7dSMauro Carvalho Chehab Execute an arbitrary command, handling errors. 2087649db7dSMauro Carvalho Chehab 2097649db7dSMauro Carvalho Chehab Please notice that this class is not thread safe 2107649db7dSMauro Carvalho Chehab """ 2117649db7dSMauro Carvalho Chehab 2127649db7dSMauro Carvalho Chehab self.stdout = "" 2137649db7dSMauro Carvalho Chehab self.stderr = "" 2147649db7dSMauro Carvalho Chehab 215fb1e8d12SMauro Carvalho Chehab self.log("$ " + " ".join(cmd), verbose) 2167649db7dSMauro Carvalho Chehab 2177649db7dSMauro Carvalho Chehab proc = await asyncio.create_subprocess_exec(cmd[0], 2187649db7dSMauro Carvalho Chehab *cmd[1:], 2197649db7dSMauro Carvalho Chehab env=env, 2207649db7dSMauro Carvalho Chehab stdout=asyncio.subprocess.PIPE, 2217649db7dSMauro Carvalho Chehab stderr=asyncio.subprocess.PIPE) 2227649db7dSMauro Carvalho Chehab 2237649db7dSMauro Carvalho Chehab # Handle input and output in realtime 2247649db7dSMauro Carvalho Chehab await asyncio.gather( 2257649db7dSMauro Carvalho Chehab self._read(proc.stdout, verbose, True), 2267649db7dSMauro Carvalho Chehab self._read(proc.stderr, verbose, False), 2277649db7dSMauro Carvalho Chehab ) 2287649db7dSMauro Carvalho Chehab 2297649db7dSMauro Carvalho Chehab await proc.wait() 2307649db7dSMauro Carvalho Chehab 2317649db7dSMauro Carvalho Chehab if check and proc.returncode > 0: 2327649db7dSMauro Carvalho Chehab raise subprocess.CalledProcessError(returncode=proc.returncode, 2337649db7dSMauro Carvalho Chehab cmd=" ".join(cmd), 2347649db7dSMauro Carvalho Chehab output=self.stdout, 2357649db7dSMauro Carvalho Chehab stderr=self.stderr) 2367649db7dSMauro Carvalho Chehab 2377649db7dSMauro Carvalho Chehab if capture_output: 2387649db7dSMauro Carvalho Chehab if proc.returncode > 0: 239fb1e8d12SMauro Carvalho Chehab self.log(f"Error {proc.returncode}", verbose=True, is_info=False) 2407649db7dSMauro Carvalho Chehab return "" 2417649db7dSMauro Carvalho Chehab 2427649db7dSMauro Carvalho Chehab return self.output 2437649db7dSMauro Carvalho Chehab 2447649db7dSMauro Carvalho Chehab ret = subprocess.CompletedProcess(args=cmd, 2457649db7dSMauro Carvalho Chehab returncode=proc.returncode, 2467649db7dSMauro Carvalho Chehab stdout=self.stdout, 2477649db7dSMauro Carvalho Chehab stderr=self.stderr) 2487649db7dSMauro Carvalho Chehab 2497649db7dSMauro Carvalho Chehab return ret 2507649db7dSMauro Carvalho Chehab 2517649db7dSMauro Carvalho Chehab 2527649db7dSMauro Carvalho Chehabclass SphinxVenv: 2537649db7dSMauro Carvalho Chehab """ 2547649db7dSMauro Carvalho Chehab Installs Sphinx on one virtual env per Sphinx version with a minimal 2557649db7dSMauro Carvalho Chehab set of dependencies, adjusting them to each specific version. 2567649db7dSMauro Carvalho Chehab """ 2577649db7dSMauro Carvalho Chehab 2587649db7dSMauro Carvalho Chehab def __init__(self): 2597649db7dSMauro Carvalho Chehab """Initialize instance variables""" 2607649db7dSMauro Carvalho Chehab 2617649db7dSMauro Carvalho Chehab self.built_time = {} 2627649db7dSMauro Carvalho Chehab self.first_run = True 2637649db7dSMauro Carvalho Chehab 264fb1e8d12SMauro Carvalho Chehab async def _handle_version(self, args, fp, 265fb1e8d12SMauro Carvalho Chehab cur_ver, cur_requirements, python_bin): 2667649db7dSMauro Carvalho Chehab """Handle a single Sphinx version""" 2677649db7dSMauro Carvalho Chehab 268fb1e8d12SMauro Carvalho Chehab cmd = AsyncCommands(fp) 2697649db7dSMauro Carvalho Chehab 2707649db7dSMauro Carvalho Chehab ver = ".".join(map(str, cur_ver)) 2717649db7dSMauro Carvalho Chehab 272*bb4c5c50SMauro Carvalho Chehab if not self.first_run and args.wait_input and args.build: 2737649db7dSMauro Carvalho Chehab ret = input("Press Enter to continue or 'a' to abort: ").strip().lower() 2747649db7dSMauro Carvalho Chehab if ret == "a": 2757649db7dSMauro Carvalho Chehab print("Aborted.") 2767649db7dSMauro Carvalho Chehab sys.exit() 2777649db7dSMauro Carvalho Chehab else: 2787649db7dSMauro Carvalho Chehab self.first_run = False 2797649db7dSMauro Carvalho Chehab 2807649db7dSMauro Carvalho Chehab venv_dir = f"Sphinx_{ver}" 2817649db7dSMauro Carvalho Chehab req_file = f"requirements_{ver}.txt" 2827649db7dSMauro Carvalho Chehab 283fb1e8d12SMauro Carvalho Chehab cmd.log(f"\nSphinx {ver} with {python_bin}", verbose=True) 2847649db7dSMauro Carvalho Chehab 2857649db7dSMauro Carvalho Chehab # Create venv 286fb1e8d12SMauro Carvalho Chehab await cmd.run([python_bin, "-m", "venv", venv_dir], 287fb1e8d12SMauro Carvalho Chehab verbose=args.verbose, check=True) 2887649db7dSMauro Carvalho Chehab pip = os.path.join(venv_dir, "bin/pip") 2897649db7dSMauro Carvalho Chehab 2907649db7dSMauro Carvalho Chehab # Create install list 2917649db7dSMauro Carvalho Chehab reqs = [] 2927649db7dSMauro Carvalho Chehab for pkg, verstr in cur_requirements.items(): 2937649db7dSMauro Carvalho Chehab reqs.append(f"{pkg}=={verstr}") 2947649db7dSMauro Carvalho Chehab 2957649db7dSMauro Carvalho Chehab reqs.append(f"Sphinx=={ver}") 2967649db7dSMauro Carvalho Chehab 297fb1e8d12SMauro Carvalho Chehab await cmd.run([pip, "install"] + reqs, check=True, verbose=args.verbose) 2987649db7dSMauro Carvalho Chehab 2997649db7dSMauro Carvalho Chehab # Freeze environment 3007649db7dSMauro Carvalho Chehab result = await cmd.run([pip, "freeze"], verbose=False, check=True) 3017649db7dSMauro Carvalho Chehab 3027649db7dSMauro Carvalho Chehab # Pip install succeeded. Write requirements file 303*bb4c5c50SMauro Carvalho Chehab if args.req_file: 3047649db7dSMauro Carvalho Chehab with open(req_file, "w", encoding="utf-8") as fp: 3057649db7dSMauro Carvalho Chehab fp.write(result.stdout) 3067649db7dSMauro Carvalho Chehab 307*bb4c5c50SMauro Carvalho Chehab if args.build: 3087649db7dSMauro Carvalho Chehab start_time = time.time() 3097649db7dSMauro Carvalho Chehab 3107649db7dSMauro Carvalho Chehab # Prepare a venv environment 3117649db7dSMauro Carvalho Chehab env = os.environ.copy() 3127649db7dSMauro Carvalho Chehab bin_dir = os.path.join(venv_dir, "bin") 3137649db7dSMauro Carvalho Chehab env["PATH"] = bin_dir + ":" + env["PATH"] 3147649db7dSMauro Carvalho Chehab env["VIRTUAL_ENV"] = venv_dir 3157649db7dSMauro Carvalho Chehab if "PYTHONHOME" in env: 3167649db7dSMauro Carvalho Chehab del env["PYTHONHOME"] 3177649db7dSMauro Carvalho Chehab 3187649db7dSMauro Carvalho Chehab # Test doc build 3197649db7dSMauro Carvalho Chehab await cmd.run(["make", "cleandocs"], env=env, check=True) 320*bb4c5c50SMauro Carvalho Chehab make = ["make"] 321*bb4c5c50SMauro Carvalho Chehab 322*bb4c5c50SMauro Carvalho Chehab if args.output: 323*bb4c5c50SMauro Carvalho Chehab sphinx_build = os.path.realpath(f"{bin_dir}/sphinx-build") 324*bb4c5c50SMauro Carvalho Chehab make += [f"O={args.output}", f"SPHINXBUILD={sphinx_build}"] 325*bb4c5c50SMauro Carvalho Chehab 326*bb4c5c50SMauro Carvalho Chehab if args.make_args: 327*bb4c5c50SMauro Carvalho Chehab make += args.make_args 328*bb4c5c50SMauro Carvalho Chehab 329*bb4c5c50SMauro Carvalho Chehab make += args.targets 3307649db7dSMauro Carvalho Chehab 331fb1e8d12SMauro Carvalho Chehab if args.verbose: 3320e93f124SMauro Carvalho Chehab cmd.log(f". {bin_dir}/activate", verbose=True) 333fb1e8d12SMauro Carvalho Chehab await cmd.run(make, env=env, check=True, verbose=True) 334fb1e8d12SMauro Carvalho Chehab if args.verbose: 3350e93f124SMauro Carvalho Chehab cmd.log("deactivate", verbose=True) 3367649db7dSMauro Carvalho Chehab 3377649db7dSMauro Carvalho Chehab end_time = time.time() 3387649db7dSMauro Carvalho Chehab elapsed_time = end_time - start_time 3397649db7dSMauro Carvalho Chehab hours, minutes = divmod(elapsed_time, 3600) 3407649db7dSMauro Carvalho Chehab minutes, seconds = divmod(minutes, 60) 3417649db7dSMauro Carvalho Chehab 3427649db7dSMauro Carvalho Chehab hours = int(hours) 3437649db7dSMauro Carvalho Chehab minutes = int(minutes) 3447649db7dSMauro Carvalho Chehab seconds = int(seconds) 3457649db7dSMauro Carvalho Chehab 3467649db7dSMauro Carvalho Chehab self.built_time[ver] = f"{hours:02d}:{minutes:02d}:{seconds:02d}" 3477649db7dSMauro Carvalho Chehab 348fb1e8d12SMauro Carvalho Chehab cmd.log(f"Finished doc build for Sphinx {ver}. Elapsed time: {self.built_time[ver]}", verbose=True) 3497649db7dSMauro Carvalho Chehab 3507649db7dSMauro Carvalho Chehab async def run(self, args): 3517649db7dSMauro Carvalho Chehab """ 3527649db7dSMauro Carvalho Chehab Navigate though multiple Sphinx versions, handling each of them 3537649db7dSMauro Carvalho Chehab on a loop. 3547649db7dSMauro Carvalho Chehab """ 3557649db7dSMauro Carvalho Chehab 356fb1e8d12SMauro Carvalho Chehab if args.log: 357fb1e8d12SMauro Carvalho Chehab fp = open(args.log, "w", encoding="utf-8") 358fb1e8d12SMauro Carvalho Chehab if not args.verbose: 359fb1e8d12SMauro Carvalho Chehab args.verbose = False 360fb1e8d12SMauro Carvalho Chehab else: 361fb1e8d12SMauro Carvalho Chehab fp = None 362fb1e8d12SMauro Carvalho Chehab if not args.verbose: 363fb1e8d12SMauro Carvalho Chehab args.verbose = True 364fb1e8d12SMauro Carvalho Chehab 3657649db7dSMauro Carvalho Chehab cur_requirements = {} 3663fa60d28SMauro Carvalho Chehab python_bin = min_python_bin 3677649db7dSMauro Carvalho Chehab 368791b9b03SMauro Carvalho Chehab vers = set(SPHINX_REQUIREMENTS.keys()) | set(args.versions) 369791b9b03SMauro Carvalho Chehab 370791b9b03SMauro Carvalho Chehab for cur_ver in sorted(vers): 371791b9b03SMauro Carvalho Chehab if cur_ver in SPHINX_REQUIREMENTS: 372791b9b03SMauro Carvalho Chehab new_reqs = SPHINX_REQUIREMENTS[cur_ver] 3737649db7dSMauro Carvalho Chehab cur_requirements.update(new_reqs) 3747649db7dSMauro Carvalho Chehab 3757649db7dSMauro Carvalho Chehab if cur_ver in PYTHON_VER_CHANGES: # pylint: disable=R1715 3767649db7dSMauro Carvalho Chehab python_bin = PYTHON_VER_CHANGES[cur_ver] 3777649db7dSMauro Carvalho Chehab 378791b9b03SMauro Carvalho Chehab if cur_ver not in args.versions: 379791b9b03SMauro Carvalho Chehab continue 380791b9b03SMauro Carvalho Chehab 3817649db7dSMauro Carvalho Chehab if args.min_version: 3827649db7dSMauro Carvalho Chehab if cur_ver < args.min_version: 3837649db7dSMauro Carvalho Chehab continue 3847649db7dSMauro Carvalho Chehab 3857649db7dSMauro Carvalho Chehab if args.max_version: 3867649db7dSMauro Carvalho Chehab if cur_ver > args.max_version: 3877649db7dSMauro Carvalho Chehab break 3887649db7dSMauro Carvalho Chehab 389fb1e8d12SMauro Carvalho Chehab await self._handle_version(args, fp, cur_ver, cur_requirements, 3907649db7dSMauro Carvalho Chehab python_bin) 3917649db7dSMauro Carvalho Chehab 392*bb4c5c50SMauro Carvalho Chehab if args.build: 3930e93f124SMauro Carvalho Chehab cmd = AsyncCommands(fp) 3940e93f124SMauro Carvalho Chehab cmd.log("\nSummary:", verbose=True) 3957649db7dSMauro Carvalho Chehab for ver, elapsed_time in sorted(self.built_time.items()): 3960e93f124SMauro Carvalho Chehab cmd.log(f"\tSphinx {ver} elapsed time: {elapsed_time}", 3970e93f124SMauro Carvalho Chehab verbose=True) 3987649db7dSMauro Carvalho Chehab 399fb1e8d12SMauro Carvalho Chehab if fp: 400fb1e8d12SMauro Carvalho Chehab fp.close() 4017649db7dSMauro Carvalho Chehab 40254c147f4SMauro Carvalho Chehabdef parse_version(ver_str): 40354c147f4SMauro Carvalho Chehab """Convert a version string into a tuple.""" 40454c147f4SMauro Carvalho Chehab 40554c147f4SMauro Carvalho Chehab return tuple(map(int, ver_str.split("."))) 40654c147f4SMauro Carvalho Chehab 40754c147f4SMauro Carvalho Chehab 408791b9b03SMauro Carvalho ChehabDEFAULT_VERS = " - " 409791b9b03SMauro Carvalho ChehabDEFAULT_VERS += "\n - ".join(map(lambda v: f"{v[0]}.{v[1]}.{v[2]}", 410791b9b03SMauro Carvalho Chehab DEFAULT_VERSIONS_TO_TEST)) 411791b9b03SMauro Carvalho Chehab 412791b9b03SMauro Carvalho ChehabSCRIPT = os.path.relpath(__file__) 413791b9b03SMauro Carvalho Chehab 414791b9b03SMauro Carvalho ChehabDESCRIPTION = f""" 415791b9b03SMauro Carvalho ChehabThis tool allows creating Python virtual environments for different 416791b9b03SMauro Carvalho ChehabSphinx versions that are supported by the Linux Kernel build system. 417791b9b03SMauro Carvalho Chehab 418791b9b03SMauro Carvalho ChehabBesides creating the virtual environment, it can also test building 419*bb4c5c50SMauro Carvalho Chehabthe documentation using "make htmldocs" (and/or other doc targets). 420791b9b03SMauro Carvalho Chehab 421791b9b03SMauro Carvalho ChehabIf called without "--versions" argument, it covers the versions shipped 422791b9b03SMauro Carvalho Chehabon major distros, plus the lowest supported version: 423791b9b03SMauro Carvalho Chehab 424791b9b03SMauro Carvalho Chehab{DEFAULT_VERS} 425791b9b03SMauro Carvalho Chehab 426791b9b03SMauro Carvalho ChehabA typical usage is to run: 427791b9b03SMauro Carvalho Chehab 428791b9b03SMauro Carvalho Chehab {SCRIPT} -m -l sphinx_builds.log 429791b9b03SMauro Carvalho Chehab 430*bb4c5c50SMauro Carvalho ChehabThis will create one virtual env for the default version set and run 431*bb4c5c50SMauro Carvalho Chehab"make htmldocs" for each version, creating a log file with the 432791b9b03SMauro Carvalho Chehabexcecuted commands on it. 433791b9b03SMauro Carvalho Chehab 434791b9b03SMauro Carvalho ChehabNOTE: The build time can be very long, specially on old versions. Also, there 435791b9b03SMauro Carvalho Chehabis a known bug with Sphinx version 6.0.x: each subprocess uses a lot of 436791b9b03SMauro Carvalho Chehabmemory. That, together with "-jauto" may cause OOM killer to cause 437791b9b03SMauro Carvalho Chehabfailures at the doc generation. To minimize the risk, you may use the 438791b9b03SMauro Carvalho Chehab"-a" command line parameter to constrain the built directories and/or 439791b9b03SMauro Carvalho Chehabreduce the number of threads from "-jauto" to, for instance, "-j4": 440791b9b03SMauro Carvalho Chehab 441791b9b03SMauro Carvalho Chehab {SCRIPT} -m -V 6.0.1 -a "SPHINXDIRS=process" "SPHINXOPTS='-j4'" 442791b9b03SMauro Carvalho Chehab 443791b9b03SMauro Carvalho Chehab""" 444791b9b03SMauro Carvalho Chehab 445*bb4c5c50SMauro Carvalho ChehabMAKE_TARGETS = [ 446*bb4c5c50SMauro Carvalho Chehab "htmldocs", 447*bb4c5c50SMauro Carvalho Chehab "texinfodocs", 448*bb4c5c50SMauro Carvalho Chehab "infodocs", 449*bb4c5c50SMauro Carvalho Chehab "latexdocs", 450*bb4c5c50SMauro Carvalho Chehab "pdfdocs", 451*bb4c5c50SMauro Carvalho Chehab "epubdocs", 452*bb4c5c50SMauro Carvalho Chehab "xmldocs", 453*bb4c5c50SMauro Carvalho Chehab] 454791b9b03SMauro Carvalho Chehab 4557649db7dSMauro Carvalho Chehabasync def main(): 4567649db7dSMauro Carvalho Chehab """Main program""" 4577649db7dSMauro Carvalho Chehab 458791b9b03SMauro Carvalho Chehab parser = argparse.ArgumentParser(description=DESCRIPTION, 459791b9b03SMauro Carvalho Chehab formatter_class=argparse.RawDescriptionHelpFormatter) 46054c147f4SMauro Carvalho Chehab 461*bb4c5c50SMauro Carvalho Chehab ver_group = parser.add_argument_group("Version range options") 462*bb4c5c50SMauro Carvalho Chehab 463*bb4c5c50SMauro Carvalho Chehab ver_group.add_argument('-V', '--versions', nargs="*", 464*bb4c5c50SMauro Carvalho Chehab default=DEFAULT_VERSIONS_TO_TEST,type=parse_version, 465*bb4c5c50SMauro Carvalho Chehab help='Sphinx versions to test') 466*bb4c5c50SMauro Carvalho Chehab ver_group.add_argument('--min-version', "--min", type=parse_version, 467*bb4c5c50SMauro Carvalho Chehab help='Sphinx minimal version') 468*bb4c5c50SMauro Carvalho Chehab ver_group.add_argument('--max-version', "--max", type=parse_version, 469*bb4c5c50SMauro Carvalho Chehab help='Sphinx maximum version') 470*bb4c5c50SMauro Carvalho Chehab ver_group.add_argument('-f', '--full', action='store_true', 471*bb4c5c50SMauro Carvalho Chehab help='Add all Sphinx (major,minor) supported versions to the version range') 472*bb4c5c50SMauro Carvalho Chehab 473*bb4c5c50SMauro Carvalho Chehab build_group = parser.add_argument_group("Build options") 474*bb4c5c50SMauro Carvalho Chehab 475*bb4c5c50SMauro Carvalho Chehab build_group.add_argument('-b', '--build', action='store_true', 476*bb4c5c50SMauro Carvalho Chehab help='Build documentation') 477*bb4c5c50SMauro Carvalho Chehab build_group.add_argument('-a', '--make-args', nargs="*", 478*bb4c5c50SMauro Carvalho Chehab help='extra arguments for make, like SPHINXDIRS=netlink/specs', 479*bb4c5c50SMauro Carvalho Chehab ) 480*bb4c5c50SMauro Carvalho Chehab build_group.add_argument('-t', '--targets', nargs="+", choices=MAKE_TARGETS, 481*bb4c5c50SMauro Carvalho Chehab default=[MAKE_TARGETS[0]], 482*bb4c5c50SMauro Carvalho Chehab help="make build targets. Default: htmldocs.") 483*bb4c5c50SMauro Carvalho Chehab build_group.add_argument("-o", '--output', 484*bb4c5c50SMauro Carvalho Chehab help="output directory for the make O=OUTPUT") 485*bb4c5c50SMauro Carvalho Chehab 486*bb4c5c50SMauro Carvalho Chehab other_group = parser.add_argument_group("Other options") 487*bb4c5c50SMauro Carvalho Chehab 488*bb4c5c50SMauro Carvalho Chehab other_group.add_argument('-r', '--req-file', action='store_true', 489*bb4c5c50SMauro Carvalho Chehab help='write a requirements.txt file') 490*bb4c5c50SMauro Carvalho Chehab other_group.add_argument('-l', '--log', 491fb1e8d12SMauro Carvalho Chehab help='Log command output on a file') 492*bb4c5c50SMauro Carvalho Chehab other_group.add_argument('-v', '--verbose', action='store_true', 493*bb4c5c50SMauro Carvalho Chehab help='Verbose all commands') 494*bb4c5c50SMauro Carvalho Chehab other_group.add_argument('-i', '--wait-input', action='store_true', 495*bb4c5c50SMauro Carvalho Chehab help='Wait for an enter before going to the next version') 49654c147f4SMauro Carvalho Chehab 49754c147f4SMauro Carvalho Chehab args = parser.parse_args() 49854c147f4SMauro Carvalho Chehab 49954c147f4SMauro Carvalho Chehab if not args.make_args: 50054c147f4SMauro Carvalho Chehab args.make_args = [] 50154c147f4SMauro Carvalho Chehab 5027649db7dSMauro Carvalho Chehab sphinx_versions = sorted(list(SPHINX_REQUIREMENTS.keys())) 50354c147f4SMauro Carvalho Chehab 504791b9b03SMauro Carvalho Chehab if args.full: 505791b9b03SMauro Carvalho Chehab args.versions += list(SPHINX_REQUIREMENTS.keys()) 50654c147f4SMauro Carvalho Chehab 5077649db7dSMauro Carvalho Chehab venv = SphinxVenv() 5087649db7dSMauro Carvalho Chehab await venv.run(args) 50954c147f4SMauro Carvalho Chehab 51054c147f4SMauro Carvalho Chehab 5117649db7dSMauro Carvalho Chehab# Call main method 5127649db7dSMauro Carvalho Chehabif __name__ == "__main__": 5137649db7dSMauro Carvalho Chehab asyncio.run(main()) 514